From f087dfb52cb9a3a33b85b922fdaadf0514b37e46 Mon Sep 17 00:00:00 2001 From: Ethan Yonker Date: Wed, 9 Jan 2013 16:35:51 +0000 Subject: exit instead of return if sideload file creation fails A return here leaves adb sideload in a permanent loop. An exit is more appropriate for this error. Change-Id: I80fb8abae4f6378833aa75f9eaf7ec1acd44b274 Signed-off-by: Ethan Yonker --- minadbd/services.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minadbd/services.c b/minadbd/services.c index aef37f7e4..6b5e3b9a4 100644 --- a/minadbd/services.c +++ b/minadbd/services.c @@ -55,7 +55,7 @@ static void sideload_service(int s, void *cookie) if(fd < 0) { fprintf(stderr, "failed to create %s\n", ADB_SIDELOAD_FILENAME); adb_close(s); - return; + exit(1); } while(count > 0) { -- cgit v1.2.3 From 41329c5fd09812ec7c53174f3a7cf67a49f23ce7 Mon Sep 17 00:00:00 2001 From: John Reck Date: Tue, 13 Aug 2013 13:01:29 -0700 Subject: Fix libpng API usage Remove usage of deprecated methods that were removed in 1.6 Change-Id: I2a669bf5201197f60adfdbe512fd729bebb74f9c --- minui/resources.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/minui/resources.c b/minui/resources.c index 72f39fbaa..c0a9ccacb 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -93,9 +93,13 @@ int res_create_surface(const char* name, gr_surface* pSurface) { png_set_sig_bytes(png_ptr, sizeof(header)); png_read_info(png_ptr, info_ptr); - int color_type = info_ptr->color_type; - int bit_depth = info_ptr->bit_depth; - int channels = info_ptr->channels; + int color_type, bit_depth; + size_t width, height; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, + &color_type, NULL, NULL, NULL); + + int channels = png_get_channels(png_ptr, info_ptr); + if (!(bit_depth == 8 && ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) || (channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) || @@ -105,8 +109,6 @@ int res_create_surface(const char* name, gr_surface* pSurface) { goto exit; } - size_t width = info_ptr->width; - size_t height = info_ptr->height; size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width; size_t pixelSize = stride * height; @@ -246,13 +248,11 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) { 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; - - int color_type = info_ptr->color_type; - int bit_depth = info_ptr->bit_depth; - int channels = info_ptr->channels; + int color_type, bit_depth; + size_t width, height; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, + &color_type, NULL, NULL, NULL); + int channels = png_get_channels(png_ptr, info_ptr); if (!(bit_depth == 8 && (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) { -- cgit v1.2.3 From 9e805d6ca0101f6bfc458e5c1e9b48fecb495a72 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 4 Sep 2013 13:44:38 -0700 Subject: allow CheckKey to request mounting /system Also provide a default implementation of CheckKey that's reasonable for many devices (those that have power and volume keys). Change-Id: Icf6c7746ebd866152d402059dbd27fd16bd51ff8 --- Android.mk | 1 + ui.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- ui.h | 6 +++++- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/Android.mk b/Android.mk index 075fa2cfe..645a83534 100644 --- a/Android.mk +++ b/Android.mk @@ -82,6 +82,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := verifier_test LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_TAGS := tests +LOCAL_CFLAGS += -DNO_RECOVERY_MOUNT LOCAL_SRC_FILES := \ verifier_test.cpp \ verifier.cpp \ diff --git a/ui.cpp b/ui.cpp index cece02d31..5043ee528 100644 --- a/ui.cpp +++ b/ui.cpp @@ -31,6 +31,7 @@ #include #include "common.h" +#include "roots.h" #include "device.h" #include "minui/minui.h" #include "screen_ui.h" @@ -47,7 +48,10 @@ RecoveryUI::RecoveryUI() : key_queue_len(0), key_last_down(-1), key_long_press(false), - key_down_count(0) { + key_down_count(0), + consecutive_power_keys(0), + consecutive_alternate_keys(0), + last_key(-1) { pthread_mutex_init(&key_queue_mutex, NULL); pthread_cond_init(&key_queue_cond, NULL); self = this; @@ -152,6 +156,13 @@ void RecoveryUI::process_key(int key_code, int updown) { case RecoveryUI::ENQUEUE: EnqueueKey(key_code); break; + + case RecoveryUI::MOUNT_SYSTEM: +#ifndef NO_RECOVERY_MOUNT + ensure_path_mounted("/system"); + Print("Mounted /system."); +#endif + break; } } } @@ -258,8 +269,41 @@ void RecoveryUI::FlushKeys() { pthread_mutex_unlock(&key_queue_mutex); } +// The default CheckKey implementation assumes the device has power, +// volume up, and volume down keys. +// +// - Hold power and press vol-up to toggle display. +// - Press power seven times in a row to reboot. +// - Alternate vol-up and vol-down seven times to mount /system. RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) { - return RecoveryUI::ENQUEUE; + if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) { + return TOGGLE; + } + + if (key == KEY_POWER) { + ++consecutive_power_keys; + if (consecutive_power_keys >= 7) { + return REBOOT; + } + } else { + consecutive_power_keys = 0; + } + + if ((key == KEY_VOLUMEUP && + (last_key == KEY_VOLUMEDOWN || last_key == -1)) || + (key == KEY_VOLUMEDOWN && + (last_key == KEY_VOLUMEUP || last_key == -1))) { + ++consecutive_alternate_keys; + if (consecutive_alternate_keys >= 7) { + consecutive_alternate_keys = 0; + return MOUNT_SYSTEM; + } + } else { + consecutive_alternate_keys = 0; + } + last_key = key; + + return ENQUEUE; } void RecoveryUI::NextCheckKeyIsLong(bool is_long_press) { diff --git a/ui.h b/ui.h index 6c8987a33..d85fc659b 100644 --- a/ui.h +++ b/ui.h @@ -77,7 +77,7 @@ class RecoveryUI { // Return value indicates whether an immediate operation should be // triggered (toggling the display, rebooting the device), or if // the key should be enqueued for use by the main thread. - enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE }; + enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE, MOUNT_SYSTEM }; virtual KeyAction CheckKey(int key); // Called immediately before each call to CheckKey(), tell you if @@ -121,6 +121,10 @@ private: int key_down_count; // under key_queue_mutex int rel_sum; + int consecutive_power_keys; + int consecutive_alternate_keys; + int last_key; + typedef struct { RecoveryUI* ui; int key_code; -- cgit v1.2.3 From 08ef9a957027183dcf55e432441e8fb0d5299aba Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Wed, 11 Sep 2013 11:37:10 -0700 Subject: updater: Delete dead code set_perm and set_perm_recursive are no longer used. Delete. Change-Id: I3bb40b934b6c093b24b88aa4ed6f3c7de2bb52f0 --- minzip/DirUtil.c | 58 ------------------------------------- minzip/DirUtil.h | 8 ----- updater/install.c | 87 ------------------------------------------------------- 3 files changed, 153 deletions(-) diff --git a/minzip/DirUtil.c b/minzip/DirUtil.c index 8dd5da1da..fe2c880ac 100644 --- a/minzip/DirUtil.c +++ b/minzip/DirUtil.c @@ -234,61 +234,3 @@ dirUnlinkHierarchy(const char *path) /* delete target directory */ return rmdir(path); } - -int -dirSetHierarchyPermissions(const char *path, - int uid, int gid, int dirMode, int fileMode) -{ - struct stat st; - if (lstat(path, &st)) { - return -1; - } - - /* ignore symlinks */ - if (S_ISLNK(st.st_mode)) { - return 0; - } - - /* directories and files get different permissions */ - if (chown(path, uid, gid) || - chmod(path, S_ISDIR(st.st_mode) ? dirMode : fileMode)) { - return -1; - } - - /* recurse over directory components */ - if (S_ISDIR(st.st_mode)) { - DIR *dir = opendir(path); - if (dir == NULL) { - return -1; - } - - errno = 0; - const struct dirent *de; - while (errno == 0 && (de = readdir(dir)) != NULL) { - if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) { - continue; - } - - char dn[PATH_MAX]; - snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name); - if (!dirSetHierarchyPermissions(dn, uid, gid, dirMode, fileMode)) { - errno = 0; - } else if (errno == 0) { - errno = -1; - } - } - - if (errno != 0) { - int save = errno; - closedir(dir); - errno = save; - return -1; - } - - if (closedir(dir)) { - return -1; - } - } - - return 0; -} diff --git a/minzip/DirUtil.h b/minzip/DirUtil.h index a5cfa761b..85a00128b 100644 --- a/minzip/DirUtil.h +++ b/minzip/DirUtil.h @@ -48,14 +48,6 @@ int dirCreateHierarchy(const char *path, int mode, */ int dirUnlinkHierarchy(const char *path); -/* chown -R : - * chmod -R - * - * Sets directories to and files to . Skips symlinks. - */ -int dirSetHierarchyPermissions(const char *path, - int uid, int gid, int dirMode, int fileMode); - #ifdef __cplusplus } #endif diff --git a/updater/install.c b/updater/install.c index 0a859452a..061aa7b5b 100644 --- a/updater/install.c +++ b/updater/install.c @@ -524,88 +524,6 @@ Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { return StringValue(strdup("")); } - -Value* 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, min_args, argc); - } - - char** args = ReadVarArgs(state, argc, argv); - if (args == NULL) return NULL; - - char* end; - int i; - int bad = 0; - - 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) { - printf("%s: chown of %s to %d %d failed: %s\n", - name, args[i], uid, gid, strerror(errno)); - ++bad; - } - if (chmod(args[i], mode) < 0) { - printf("%s: chmod of %s to %o failed: %s\n", - name, args[i], mode, strerror(errno)); - ++bad; - } - } - } - result = strdup(""); - -done: - for (i = 0; i < argc; ++i) { - free(args[i]); - } - free(args); - - if (bad) { - free(result); - return ErrorAbort(state, "%s: some changes failed", name); - } - return StringValue(result); -} - struct perm_parsed_args { bool has_uid; uid_t uid; @@ -1398,11 +1316,6 @@ void RegisterInstallFunctions() { RegisterFunction("package_extract_file", PackageExtractFileFn); RegisterFunction("symlink", SymlinkFn); - // Maybe, at some future point, we can delete these functions? They have been - // replaced by perm_set and perm_set_recursive. - RegisterFunction("set_perm", SetPermFn); - RegisterFunction("set_perm_recursive", SetPermFn); - // Usage: // set_metadata("filename", "key1", "value1", "key2", "value2", ...) // Example: -- cgit v1.2.3 From 1fc89d4c84e512b484d82d971d997bdb0aef40e2 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 10 Sep 2013 16:52:54 -0700 Subject: minui: convert ev_*() event interface to epoll Help enable external main loop combined with ev_*() key event processing. Specify EPOLLWAKEUP to hold a wakelock on any event, assuming this is needed (may need to make this optional). Convert callback events parameter to unsigned int. Change-Id: Ib5e09abbd7724ffd830e2cf8e25e7eb59d3aa072 --- minui/events.c | 81 ++++++++++++++++++++++++++++++++++++++++------------------ minui/minui.h | 4 +-- 2 files changed, 58 insertions(+), 27 deletions(-) diff --git a/minui/events.c b/minui/events.c index 2918afaa8..fe364bf01 100644 --- a/minui/events.c +++ b/minui/events.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include @@ -34,11 +34,15 @@ ((array)[(bit)/BITS_PER_LONG] & (1 << ((bit) % BITS_PER_LONG))) struct fd_info { + int fd; ev_callback cb; void *data; }; -static struct pollfd ev_fds[MAX_DEVICES + MAX_MISC_FDS]; +static int epollfd; +static struct epoll_event polledevents[MAX_DEVICES + MAX_MISC_FDS]; +static int npolledevents; + static struct fd_info ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS]; static unsigned ev_count = 0; @@ -50,6 +54,12 @@ int ev_init(ev_callback input_cb, void *data) DIR *dir; struct dirent *de; int fd; + struct epoll_event ev; + bool epollctlfail = false; + + epollfd = epoll_create(MAX_DEVICES + MAX_MISC_FDS); + if (epollfd == -1) + return -1; dir = opendir("/dev/input"); if(dir != 0) { @@ -74,8 +84,15 @@ int ev_init(ev_callback input_cb, void *data) continue; } - ev_fds[ev_count].fd = fd; - ev_fds[ev_count].events = POLLIN; + ev.events = EPOLLIN | EPOLLWAKEUP; + ev.data.ptr = (void *)&ev_fdinfo[ev_count]; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev)) { + close(fd); + epollctlfail = true; + continue; + } + + ev_fdinfo[ev_count].fd = fd; ev_fdinfo[ev_count].cb = input_cb; ev_fdinfo[ev_count].data = data; ev_count++; @@ -84,59 +101,73 @@ int ev_init(ev_callback input_cb, void *data) } } + if (epollctlfail && !ev_count) { + close(epollfd); + epollfd = -1; + return -1; + } + return 0; } int ev_add_fd(int fd, ev_callback cb, void *data) { + struct epoll_event ev; + int ret; + if (ev_misc_count == MAX_MISC_FDS || cb == NULL) return -1; - ev_fds[ev_count].fd = fd; - ev_fds[ev_count].events = POLLIN; - ev_fdinfo[ev_count].cb = cb; - ev_fdinfo[ev_count].data = data; - ev_count++; - ev_misc_count++; - return 0; + ev.events = EPOLLIN | EPOLLWAKEUP; + ev.data.ptr = (void *)&ev_fdinfo[ev_count]; + ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev); + if (!ret) { + ev_fdinfo[ev_count].fd = fd; + ev_fdinfo[ev_count].cb = cb; + ev_fdinfo[ev_count].data = data; + ev_count++; + ev_misc_count++; + } + + return ret; } void ev_exit(void) { while (ev_count > 0) { - close(ev_fds[--ev_count].fd); + close(ev_fdinfo[--ev_count].fd); } ev_misc_count = 0; ev_dev_count = 0; + close(epollfd); } int ev_wait(int timeout) { - int r; - - r = poll(ev_fds, ev_count, timeout); - if (r <= 0) + npolledevents = epoll_wait(epollfd, polledevents, ev_count, timeout); + if (npolledevents <= 0) return -1; return 0; } void ev_dispatch(void) { - unsigned n; + int n; int ret; - for (n = 0; n < ev_count; n++) { - ev_callback cb = ev_fdinfo[n].cb; - if (cb && (ev_fds[n].revents & ev_fds[n].events)) - cb(ev_fds[n].fd, ev_fds[n].revents, ev_fdinfo[n].data); + for (n = 0; n < npolledevents; n++) { + struct fd_info *fdi = polledevents[n].data.ptr; + ev_callback cb = fdi->cb; + if (cb) + cb(fdi->fd, polledevents[n].events, fdi->data); } } -int ev_get_input(int fd, short revents, struct input_event *ev) +int ev_get_input(int fd, unsigned int epevents, struct input_event *ev) { int r; - if (revents & POLLIN) { + if (epevents & EPOLLIN) { r = read(fd, ev, sizeof(*ev)); if (r == sizeof(*ev)) return 0; @@ -157,11 +188,11 @@ int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data) memset(key_bits, 0, sizeof(key_bits)); memset(ev_bits, 0, sizeof(ev_bits)); - ret = ioctl(ev_fds[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits); + ret = ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits); if (ret < 0 || !test_bit(EV_KEY, ev_bits)) continue; - ret = ioctl(ev_fds[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits); + ret = ioctl(ev_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits); if (ret < 0) continue; diff --git a/minui/minui.h b/minui/minui.h index 1b8dd059b..805c58bed 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -50,7 +50,7 @@ unsigned int gr_get_height(gr_surface surface); // see http://www.mjmwired.net/kernel/Documentation/input/ for info. struct input_event; -typedef int (*ev_callback)(int fd, short revents, void *data); +typedef int (*ev_callback)(int fd, unsigned int epevents, void *data); typedef int (*ev_set_key_callback)(int code, int value, void *data); int ev_init(ev_callback input_cb, void *data); @@ -65,7 +65,7 @@ int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data); */ int ev_wait(int timeout); -int ev_get_input(int fd, short revents, struct input_event *ev); +int ev_get_input(int fd, unsigned int epevents, struct input_event *ev); void ev_dispatch(void); // Resources -- cgit v1.2.3 From e7265df3523d27c9f59829c858de256cf063da26 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 10 Sep 2013 16:53:12 -0700 Subject: recovery: ui changes for ev_*() switch to epoll Convert callback events parameter to unsigned int. Change-Id: Ife0e983f307c07bf4aca807d70574aeb20c460cd --- ui.cpp | 4 ++-- ui.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui.cpp b/ui.cpp index 5043ee528..08c291ddc 100644 --- a/ui.cpp +++ b/ui.cpp @@ -63,12 +63,12 @@ void RecoveryUI::Init() { } -int RecoveryUI::input_callback(int fd, short revents, void* data) +int RecoveryUI::input_callback(int fd, unsigned int epevents, void* data) { struct input_event ev; int ret; - ret = ev_get_input(fd, revents, &ev); + ret = ev_get_input(fd, epevents, &ev); if (ret) return -1; diff --git a/ui.h b/ui.h index d85fc659b..22f3e5c53 100644 --- a/ui.h +++ b/ui.h @@ -134,7 +134,7 @@ private: pthread_t input_t; static void* input_thread(void* cookie); - static int input_callback(int fd, short revents, void* data); + static int input_callback(int fd, unsigned int epevents, void* data); void process_key(int key_code, int updown); bool usb_connected(); -- cgit v1.2.3 From 4665ede960301144eeebcf6b145ac83fd9d2c778 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 10 Sep 2013 17:03:45 -0700 Subject: minui: add ev_get_epollfd() to retrieve epoll file descriptor To allow use of ev_* functions with an external main loop. Change-Id: If73717b64d7c455ca726b90a815a31c1edf52544 --- minui/events.c | 5 +++++ minui/minui.h | 1 + 2 files changed, 6 insertions(+) diff --git a/minui/events.c b/minui/events.c index fe364bf01..008a7d8d8 100644 --- a/minui/events.c +++ b/minui/events.c @@ -132,6 +132,11 @@ int ev_add_fd(int fd, ev_callback cb, void *data) return ret; } +int ev_get_epollfd(void) +{ + return epollfd; +} + void ev_exit(void) { while (ev_count > 0) { diff --git a/minui/minui.h b/minui/minui.h index 805c58bed..ab08202f4 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -67,6 +67,7 @@ int ev_wait(int timeout); int ev_get_input(int fd, unsigned int epevents, struct input_event *ev); void ev_dispatch(void); +int ev_get_epollfd(void); // Resources -- cgit v1.2.3 From be96809d181f587255176add2aa298adaaec69e4 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 13 Sep 2013 16:34:12 -0700 Subject: start healthd in recovery mode Change-Id: I431ece69b6856fd1ea6079c38cdeb593c15d7385 --- etc/init.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/init.rc b/etc/init.rc index 175489066..9b2a99143 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -44,7 +44,7 @@ on property:sys.powerctl=* service ueventd /sbin/ueventd critical -service healthd /sbin/healthd -n +service healthd /sbin/healthd -r critical service recovery /sbin/recovery -- cgit v1.2.3 From 46ab1b6138bf911120280f1095ff9479dceee3df Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Wed, 11 Sep 2013 11:37:10 -0700 Subject: updater: Delete dead code set_perm and set_perm_recursive are no longer used. Delete. (cherry picked from commit 08ef9a957027183dcf55e432441e8fb0d5299aba) Change-Id: I1bcc90ae19af9df4f0705496c5876987159f75ac --- minzip/DirUtil.c | 58 ------------------------------------- minzip/DirUtil.h | 8 ----- updater/install.c | 87 ------------------------------------------------------- 3 files changed, 153 deletions(-) diff --git a/minzip/DirUtil.c b/minzip/DirUtil.c index 8dd5da1da..fe2c880ac 100644 --- a/minzip/DirUtil.c +++ b/minzip/DirUtil.c @@ -234,61 +234,3 @@ dirUnlinkHierarchy(const char *path) /* delete target directory */ return rmdir(path); } - -int -dirSetHierarchyPermissions(const char *path, - int uid, int gid, int dirMode, int fileMode) -{ - struct stat st; - if (lstat(path, &st)) { - return -1; - } - - /* ignore symlinks */ - if (S_ISLNK(st.st_mode)) { - return 0; - } - - /* directories and files get different permissions */ - if (chown(path, uid, gid) || - chmod(path, S_ISDIR(st.st_mode) ? dirMode : fileMode)) { - return -1; - } - - /* recurse over directory components */ - if (S_ISDIR(st.st_mode)) { - DIR *dir = opendir(path); - if (dir == NULL) { - return -1; - } - - errno = 0; - const struct dirent *de; - while (errno == 0 && (de = readdir(dir)) != NULL) { - if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) { - continue; - } - - char dn[PATH_MAX]; - snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name); - if (!dirSetHierarchyPermissions(dn, uid, gid, dirMode, fileMode)) { - errno = 0; - } else if (errno == 0) { - errno = -1; - } - } - - if (errno != 0) { - int save = errno; - closedir(dir); - errno = save; - return -1; - } - - if (closedir(dir)) { - return -1; - } - } - - return 0; -} diff --git a/minzip/DirUtil.h b/minzip/DirUtil.h index a5cfa761b..85a00128b 100644 --- a/minzip/DirUtil.h +++ b/minzip/DirUtil.h @@ -48,14 +48,6 @@ int dirCreateHierarchy(const char *path, int mode, */ int dirUnlinkHierarchy(const char *path); -/* chown -R : - * chmod -R - * - * Sets directories to and files to . Skips symlinks. - */ -int dirSetHierarchyPermissions(const char *path, - int uid, int gid, int dirMode, int fileMode); - #ifdef __cplusplus } #endif diff --git a/updater/install.c b/updater/install.c index 0a859452a..061aa7b5b 100644 --- a/updater/install.c +++ b/updater/install.c @@ -524,88 +524,6 @@ Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { return StringValue(strdup("")); } - -Value* 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, min_args, argc); - } - - char** args = ReadVarArgs(state, argc, argv); - if (args == NULL) return NULL; - - char* end; - int i; - int bad = 0; - - 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) { - printf("%s: chown of %s to %d %d failed: %s\n", - name, args[i], uid, gid, strerror(errno)); - ++bad; - } - if (chmod(args[i], mode) < 0) { - printf("%s: chmod of %s to %o failed: %s\n", - name, args[i], mode, strerror(errno)); - ++bad; - } - } - } - result = strdup(""); - -done: - for (i = 0; i < argc; ++i) { - free(args[i]); - } - free(args); - - if (bad) { - free(result); - return ErrorAbort(state, "%s: some changes failed", name); - } - return StringValue(result); -} - struct perm_parsed_args { bool has_uid; uid_t uid; @@ -1398,11 +1316,6 @@ void RegisterInstallFunctions() { RegisterFunction("package_extract_file", PackageExtractFileFn); RegisterFunction("symlink", SymlinkFn); - // Maybe, at some future point, we can delete these functions? They have been - // replaced by perm_set and perm_set_recursive. - RegisterFunction("set_perm", SetPermFn); - RegisterFunction("set_perm_recursive", SetPermFn); - // Usage: // set_metadata("filename", "key1", "value1", "key2", "value2", ...) // Example: -- cgit v1.2.3 From a5ef19fabd10428ccff2055455ef1a55dfdc5fa0 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 17 Sep 2013 13:39:10 -0700 Subject: recovery: fix epoll events type to uint32_t Change-Id: I5db9987102201c18821acb45d1f824e9865a1451 --- minui/events.c | 2 +- minui/minui.h | 4 ++-- ui.cpp | 2 +- ui.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/minui/events.c b/minui/events.c index 008a7d8d8..df7dad448 100644 --- a/minui/events.c +++ b/minui/events.c @@ -168,7 +168,7 @@ void ev_dispatch(void) } } -int ev_get_input(int fd, unsigned int epevents, struct input_event *ev) +int ev_get_input(int fd, uint32_t epevents, struct input_event *ev) { int r; diff --git a/minui/minui.h b/minui/minui.h index ab08202f4..573dd71f4 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -50,7 +50,7 @@ unsigned int gr_get_height(gr_surface surface); // see http://www.mjmwired.net/kernel/Documentation/input/ for info. struct input_event; -typedef int (*ev_callback)(int fd, unsigned int epevents, void *data); +typedef int (*ev_callback)(int fd, uint32_t epevents, void *data); typedef int (*ev_set_key_callback)(int code, int value, void *data); int ev_init(ev_callback input_cb, void *data); @@ -65,7 +65,7 @@ int ev_sync_key_state(ev_set_key_callback set_key_cb, void *data); */ int ev_wait(int timeout); -int ev_get_input(int fd, unsigned int epevents, struct input_event *ev); +int ev_get_input(int fd, uint32_t epevents, struct input_event *ev); void ev_dispatch(void); int ev_get_epollfd(void); diff --git a/ui.cpp b/ui.cpp index 08c291ddc..67a2500bb 100644 --- a/ui.cpp +++ b/ui.cpp @@ -63,7 +63,7 @@ void RecoveryUI::Init() { } -int RecoveryUI::input_callback(int fd, unsigned int epevents, void* data) +int RecoveryUI::input_callback(int fd, uint32_t epevents, void* data) { struct input_event ev; int ret; diff --git a/ui.h b/ui.h index 22f3e5c53..8cc1da1ba 100644 --- a/ui.h +++ b/ui.h @@ -134,7 +134,7 @@ private: pthread_t input_t; static void* input_thread(void* cookie); - static int input_callback(int fd, unsigned int epevents, void* data); + static int input_callback(int fd, uint32_t epevents, void* data); void process_key(int key_code, int updown); bool usb_connected(); -- cgit v1.2.3 From 30362a6ad132cbafde82cc57293e00679d4f86c2 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 10 Apr 2013 11:32:17 -0700 Subject: verifier: update to support certificates using SHA-256 (cherry picked from commit bac7fba02763ae5e78e8e4ba0bea727330ad953e) Change-Id: I01c38d7fea088622a8b0bbf2c833fa2d969417af --- applypatch/applypatch.c | 2 +- install.cpp | 2 +- testdata/otasigned_f4_sha256.zip | Bin 0 -> 5319 bytes testdata/otasigned_sha256.zip | Bin 0 -> 5326 bytes testdata/test_f4_sha256.x509.pem | 25 +++++++ testdata/testkey.pk8 | Bin 0 -> 1217 bytes testdata/testkey.x509.pem | 27 +++++++ testdata/testkey_sha256.x509.pem | 27 +++++++ updater/install.c | 2 +- verifier.cpp | 80 ++++++++++++++++----- verifier.h | 9 ++- verifier_test.cpp | 151 +++++++++++++++++++++------------------ verifier_test.sh | 38 +++++----- 13 files changed, 254 insertions(+), 109 deletions(-) create mode 100644 testdata/otasigned_f4_sha256.zip create mode 100644 testdata/otasigned_sha256.zip create mode 100644 testdata/test_f4_sha256.x509.pem create mode 100644 testdata/testkey.pk8 create mode 100644 testdata/testkey.x509.pem create mode 100644 testdata/testkey_sha256.x509.pem diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 0dcdce0b1..6b8da2a8a 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -101,7 +101,7 @@ int LoadFileContents(const char* filename, FileContents* file, } } - SHA(file->data, file->size, file->sha1); + SHA_hash(file->data, file->size, file->sha1); return 0; } diff --git a/install.cpp b/install.cpp index 0f3298f1d..0cb5cc7df 100644 --- a/install.cpp +++ b/install.cpp @@ -190,7 +190,7 @@ really_install_package(const char *path, int* wipe_cache) ui->Print("Opening update package...\n"); int numKeys; - RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); + Certificate* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); if (loadedKeys == NULL) { LOGE("Failed to load keys\n"); return INSTALL_CORRUPT; diff --git a/testdata/otasigned_f4_sha256.zip b/testdata/otasigned_f4_sha256.zip new file mode 100644 index 000000000..3af408c40 Binary files /dev/null and b/testdata/otasigned_f4_sha256.zip differ diff --git a/testdata/otasigned_sha256.zip b/testdata/otasigned_sha256.zip new file mode 100644 index 000000000..0ed4409b3 Binary files /dev/null and b/testdata/otasigned_sha256.zip differ diff --git a/testdata/test_f4_sha256.x509.pem b/testdata/test_f4_sha256.x509.pem new file mode 100644 index 000000000..9d5376b45 --- /dev/null +++ b/testdata/test_f4_sha256.x509.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIJAKhkCO1dDYMaMA0GCSqGSIb3DQEBCwUAMG8xCzAJBgNV +BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBW +aWV3MQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMT +B1Rlc3QxMjMwHhcNMTMwNDEwMTcyMzUyWhcNMTMwNTEwMTcyMzUyWjBvMQswCQYD +VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g +VmlldzEPMA0GA1UEChMGR29vZ2xlMRAwDgYDVQQLEwdBbmRyb2lkMRAwDgYDVQQD +EwdUZXN0MTIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu8WwMN9x +4Mz7YgkG2qy9g8/kl5ZoYrUM0ApHhaITAcL7RXLZaNipCf0w/YjYTQgj+75MK30x +TsnPeWNOEwA62gkHrZyyWfxBRO6kBYuIuI4roGDBJOmKQ1OEaDeIRKu7q5V8v3Cs +0wQDAQWTbhpxBZr9UYFgJUg8XWBfPrGJLVwsoiy4xrMhoTlNZKHfwOMMqVtSHkZX +qydYrcIzyjh+TO0e/xSNQ8MMRRbtqWgCHN6Rzhog3IHZu0RaPoukariopjXM/s0V +gTm3rHDHCOpna2pNblyiFlvbkoCs769mtNmx/yrDShO30jg/xaG8RypKDvTChzOT +oWW/XQ5VEXjbHwIDAQABo4HUMIHRMB0GA1UdDgQWBBRlT2dEZJY1tmUM8mZ0xnhS +GdD9TTCBoQYDVR0jBIGZMIGWgBRlT2dEZJY1tmUM8mZ0xnhSGdD9TaFzpHEwbzEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50 +YWluIFZpZXcxDzANBgNVBAoTBkdvb2dsZTEQMA4GA1UECxMHQW5kcm9pZDEQMA4G +A1UEAxMHVGVzdDEyM4IJAKhkCO1dDYMaMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN +AQELBQADggEBAKWWQ9S0V9wWjrMJe8exj1gklwD1Ysi0vi+h2tfixahelrpsNkWi +EFjoUSHEkW9ThLmtui646uAlwSiWtSn1XkGGmIJ3s+gmAFUcMc0CaK0dgoq/M9zn +fQ0Vkzc1tK4MLsf+CbPDywPycb6+T3dBkerbWn9GUpjGl1ANWlciXZZ3657m61sL +HhwUOBxbZZ6sYP4ed2SVCf45GgMyJ0VoUg5yI2JzPAgOkGfeEIPVXE1M94edJY4G +8eHYvXovJZwXvKFI+ZyS0KBPx8cpfw89RB9qmkxqNBIm8qWb3qBiuBEIPj+NF/7w +sC/Fv8NNXkVquy0xa0qdyJBABzWE18zGcXs= +-----END CERTIFICATE----- diff --git a/testdata/testkey.pk8 b/testdata/testkey.pk8 new file mode 100644 index 000000000..586c1bd5c Binary files /dev/null and b/testdata/testkey.pk8 differ diff --git a/testdata/testkey.x509.pem b/testdata/testkey.x509.pem new file mode 100644 index 000000000..e242d83e2 --- /dev/null +++ b/testdata/testkey.x509.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEqDCCA5CgAwIBAgIJAJNurL4H8gHfMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD +VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g +VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE +AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe +Fw0wODAyMjkwMTMzNDZaFw0zNTA3MTcwMTMzNDZaMIGUMQswCQYDVQQGEwJVUzET +MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G +A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p +ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI +hvcNAQEBBQADggENADCCAQgCggEBANaTGQTexgskse3HYuDZ2CU+Ps1s6x3i/waM +qOi8qM1r03hupwqnbOYOuw+ZNVn/2T53qUPn6D1LZLjk/qLT5lbx4meoG7+yMLV4 +wgRDvkxyGLhG9SEVhvA4oU6Jwr44f46+z4/Kw9oe4zDJ6pPQp8PcSvNQIg1QCAcy +4ICXF+5qBTNZ5qaU7Cyz8oSgpGbIepTYOzEJOmc3Li9kEsBubULxWBjf/gOBzAzU +RNps3cO4JFgZSAGzJWQTT7/emMkod0jb9WdqVA2BVMi7yge54kdVMxHEa5r3b97s +zI5p58ii0I54JiCUP5lyfTwE/nKZHZnfm644oLIXf6MdW2r+6R8CAQOjgfwwgfkw +HQYDVR0OBBYEFEhZAFY9JyxGrhGGBaR0GawJyowRMIHJBgNVHSMEgcEwgb6AFEhZ +AFY9JyxGrhGGBaR0GawJyowRoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE +CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH +QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG +CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAJNurL4H8gHfMAwGA1Ud +EwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHqvlozrUMRBBVEY0NqrrwFbinZa +J6cVosK0TyIUFf/azgMJWr+kLfcHCHJsIGnlw27drgQAvilFLAhLwn62oX6snb4Y +LCBOsVMR9FXYJLZW2+TcIkCRLXWG/oiVHQGo/rWuWkJgU134NDEFJCJGjDbiLCpe ++ZTWHdcwauTJ9pUbo8EvHRkU3cYfGmLaLfgn9gP+pWA7LFQNvXwBnDa6sppCccEX +31I828XzgXpJ4O+mDL1/dBd+ek8ZPUP0IgdyZm5MTYPhvVqGCHzzTy3sIeJFymwr +sBbmg2OAUNLEMO6nwmocSdN2ClirfxqCzJOLSDE4QyS9BAH6EhY6UFcOaE0= +-----END CERTIFICATE----- diff --git a/testdata/testkey_sha256.x509.pem b/testdata/testkey_sha256.x509.pem new file mode 100644 index 000000000..002ce8968 --- /dev/null +++ b/testdata/testkey_sha256.x509.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEqDCCA5CgAwIBAgIJAJNurL4H8gHfMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD +VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g +VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE +AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe +Fw0xMzA0MTAxODA1MzZaFw0xMzA1MTAxODA1MzZaMIGUMQswCQYDVQQGEwJVUzET +MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G +A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p +ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI +hvcNAQEBBQADggENADCCAQgCggEBANaTGQTexgskse3HYuDZ2CU+Ps1s6x3i/waM +qOi8qM1r03hupwqnbOYOuw+ZNVn/2T53qUPn6D1LZLjk/qLT5lbx4meoG7+yMLV4 +wgRDvkxyGLhG9SEVhvA4oU6Jwr44f46+z4/Kw9oe4zDJ6pPQp8PcSvNQIg1QCAcy +4ICXF+5qBTNZ5qaU7Cyz8oSgpGbIepTYOzEJOmc3Li9kEsBubULxWBjf/gOBzAzU +RNps3cO4JFgZSAGzJWQTT7/emMkod0jb9WdqVA2BVMi7yge54kdVMxHEa5r3b97s +zI5p58ii0I54JiCUP5lyfTwE/nKZHZnfm644oLIXf6MdW2r+6R8CAQOjgfwwgfkw +HQYDVR0OBBYEFEhZAFY9JyxGrhGGBaR0GawJyowRMIHJBgNVHSMEgcEwgb6AFEhZ +AFY9JyxGrhGGBaR0GawJyowRoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE +CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH +QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG +CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAJNurL4H8gHfMAwGA1Ud +EwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAKRVj9hOaozH1W8Wb4CNj7sCWixh +UMMZJXkxUtvUVHZGefp6MdtYiD/ZM7YRwZphm9aNhkykbHJdZ3lPzeL2csCa+sDQ +8sIzGu0/aD6p4zgIKQZmz0mZHqPGbHoLWOmA9EexRCFZ7vO/kO56ZbyhfFz2DI3S +Yez65CabErOFhNX6WukSPbV3zfsHRDD5JUStb/ko6t99HXsvIO0Ax9poj60PpCC1 +SiFzHZUY9mOnUfJFs+3NWCwKtP9nho3mZ3pJ1i+SeF6JiqbE3KHl4CDBeVGcu3CK +fiUZ8e8iXVN471Cgc5GD6Ud1pS7ifNZJsKhbETQ63KmvHCLRPi4NmP67uDE= +-----END CERTIFICATE----- diff --git a/updater/install.c b/updater/install.c index 19054236c..1fc4fd394 100644 --- a/updater/install.c +++ b/updater/install.c @@ -1057,7 +1057,7 @@ Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) { return StringValue(strdup("")); } uint8_t digest[SHA_DIGEST_SIZE]; - SHA(args[0]->data, args[0]->size, digest); + SHA_hash(args[0]->data, args[0]->size, digest); FreeValue(args[0]); if (argc == 1) { diff --git a/verifier.cpp b/verifier.cpp index 5f4c981e5..782a83863 100644 --- a/verifier.cpp +++ b/verifier.cpp @@ -20,6 +20,7 @@ #include "mincrypt/rsa.h" #include "mincrypt/sha.h" +#include "mincrypt/sha256.h" #include #include @@ -34,7 +35,7 @@ extern RecoveryUI* ui; // 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) { +int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys) { ui->SetProgress(0.0); FILE* f = fopen(path, "rb"); @@ -68,6 +69,7 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey } if (footer[2] != 0xff || footer[3] != 0xff) { + LOGE("footer is wrong\n"); fclose(f); return VERIFY_FAILURE; } @@ -139,8 +141,19 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey #define BUFFER_SIZE 4096 - SHA_CTX ctx; - SHA_init(&ctx); + bool need_sha1 = false; + bool need_sha256 = false; + for (i = 0; i < numKeys; ++i) { + switch (pKeys[i].hash_len) { + case SHA_DIGEST_SIZE: need_sha1 = true; break; + case SHA256_DIGEST_SIZE: need_sha256 = true; break; + } + } + + SHA_CTX sha1_ctx; + SHA256_CTX sha256_ctx; + SHA_init(&sha1_ctx); + SHA256_init(&sha256_ctx); unsigned char* buffer = (unsigned char*)malloc(BUFFER_SIZE); if (buffer == NULL) { LOGE("failed to alloc memory for sha1 buffer\n"); @@ -159,7 +172,8 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey fclose(f); return VERIFY_FAILURE; } - SHA_update(&ctx, buffer, size); + if (need_sha1) SHA_update(&sha1_ctx, buffer, size); + if (need_sha256) SHA256_update(&sha256_ctx, buffer, size); so_far += size; double f = so_far / (double)signed_len; if (f > frac + 0.02 || size == so_far) { @@ -170,12 +184,21 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey fclose(f); free(buffer); - const uint8_t* sha1 = SHA_final(&ctx); + const uint8_t* sha1 = SHA_final(&sha1_ctx); + const uint8_t* sha256 = SHA256_final(&sha256_ctx); + for (i = 0; i < numKeys; ++i) { + const uint8_t* hash; + switch (pKeys[i].hash_len) { + case SHA_DIGEST_SIZE: hash = sha1; break; + case SHA256_DIGEST_SIZE: hash = sha256; break; + default: continue; + } + // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that // the signing tool appends after the signature itself. - if (RSA_verify(pKeys+i, eocd + eocd_size - 6 - RSANUMBYTES, - RSANUMBYTES, sha1)) { + if (RSA_verify(pKeys[i].public_key, eocd + eocd_size - 6 - RSANUMBYTES, + RSANUMBYTES, hash, pKeys[i].hash_len)) { LOGI("whole-file signature verified against key %d\n", i); free(eocd); return VERIFY_SUCCESS; @@ -207,10 +230,19 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey // The file may contain multiple keys in this format, separated by // commas. The last key must not be followed by a comma. // +// A Certificate is a pair of an RSAPublicKey and a particular hash +// (we support SHA-1 and SHA-256; we store the hash length to signify +// which is being used). The hash used is implied by the version number. +// +// 1: 2048-bit RSA key with e=3 and SHA-1 hash +// 2: 2048-bit RSA key with e=65537 and SHA-1 hash +// 3: 2048-bit RSA key with e=3 and SHA-256 hash +// 4: 2048-bit RSA key with e=65537 and SHA-256 hash +// // Returns NULL if the file failed to parse, or if it contain zero keys. -RSAPublicKey* +Certificate* load_keys(const char* filename, int* numKeys) { - RSAPublicKey* out = NULL; + Certificate* out = NULL; *numKeys = 0; FILE* f = fopen(filename, "r"); @@ -224,24 +256,38 @@ load_keys(const char* filename, int* numKeys) { bool done = false; while (!done) { ++*numKeys; - out = (RSAPublicKey*)realloc(out, *numKeys * sizeof(RSAPublicKey)); - RSAPublicKey* key = out + (*numKeys - 1); + out = (Certificate*)realloc(out, *numKeys * sizeof(Certificate)); + Certificate* cert = out + (*numKeys - 1); + cert->public_key = (RSAPublicKey*)malloc(sizeof(RSAPublicKey)); char start_char; if (fscanf(f, " %c", &start_char) != 1) goto exit; if (start_char == '{') { // a version 1 key has no version specifier. - key->exponent = 3; + cert->public_key->exponent = 3; + cert->hash_len = SHA_DIGEST_SIZE; } else if (start_char == 'v') { int version; if (fscanf(f, "%d {", &version) != 1) goto exit; - if (version == 2) { - key->exponent = 65537; - } else { - goto exit; + switch (version) { + case 2: + cert->public_key->exponent = 65537; + cert->hash_len = SHA_DIGEST_SIZE; + break; + case 3: + cert->public_key->exponent = 3; + cert->hash_len = SHA256_DIGEST_SIZE; + break; + case 4: + cert->public_key->exponent = 65537; + cert->hash_len = SHA256_DIGEST_SIZE; + break; + default: + goto exit; } } + RSAPublicKey* key = cert->public_key; if (fscanf(f, " %i , 0x%x , { %u", &(key->len), &(key->n0inv), &(key->n[0])) != 3) { goto exit; @@ -274,7 +320,7 @@ load_keys(const char* filename, int* numKeys) { goto exit; } - LOGI("read key e=%d\n", key->exponent); + LOGI("read key e=%d hash=%d\n", key->exponent, cert->hash_len); } } diff --git a/verifier.h b/verifier.h index e9ef3b722..6ce1b44d1 100644 --- a/verifier.h +++ b/verifier.h @@ -19,12 +19,17 @@ #include "mincrypt/rsa.h" +typedef struct Certificate { + int hash_len; // SHA_DIGEST_SIZE (SHA-1) or SHA256_DIGEST_SIZE (SHA-256) + RSAPublicKey* public_key; +} Certificate; + /* Look in the file for a signature footer, and verify that it * matches one of the given keys. Return one of the constants below. */ -int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys); +int verify_file(const char* path, const Certificate *pKeys, unsigned int numKeys); -RSAPublicKey* load_keys(const char* filename, int* numKeys); +Certificate* load_keys(const char* filename, int* numKeys); #define VERIFY_SUCCESS 0 #define VERIFY_FAILURE 1 diff --git a/verifier_test.cpp b/verifier_test.cpp index 2ef52a0f7..1063cbae5 100644 --- a/verifier_test.cpp +++ b/verifier_test.cpp @@ -21,80 +21,82 @@ #include "common.h" #include "verifier.h" #include "ui.h" +#include "mincrypt/sha.h" +#include "mincrypt/sha256.h" // This is build/target/product/security/testkey.x509.pem after being // dumped out by dumpkey.jar. RSAPublicKey test_key = { 64, 0xc926ad21, - { 1795090719, 2141396315, 950055447, -1713398866, - -26044131, 1920809988, 546586521, -795969498, - 1776797858, -554906482, 1805317999, 1429410244, - 129622599, 1422441418, 1783893377, 1222374759, - -1731647369, 323993566, 28517732, 609753416, - 1826472888, 215237850, -33324596, -245884705, - -1066504894, 774857746, 154822455, -1797768399, - -1536767878, -1275951968, -1500189652, 87251430, - -1760039318, 120774784, 571297800, -599067824, - -1815042109, -483341846, -893134306, -1900097649, - -1027721089, 950095497, 555058928, 414729973, - 1136544882, -1250377212, 465547824, -236820568, - -1563171242, 1689838846, -404210357, 1048029507, - 895090649, 247140249, 178744550, -747082073, - -1129788053, 109881576, -350362881, 1044303212, - -522594267, -1309816990, -557446364, -695002876}, - { -857949815, -510492167, -1494742324, -1208744608, - 251333580, 2131931323, 512774938, 325948880, - -1637480859, 2102694287, -474399070, 792812816, - 1026422502, 2053275343, -1494078096, -1181380486, - 165549746, -21447327, -229719404, 1902789247, - 772932719, -353118870, -642223187, 216871947, - -1130566647, 1942378755, -298201445, 1055777370, - 964047799, 629391717, -2062222979, -384408304, - 191868569, -1536083459, -612150544, -1297252564, - -1592438046, -724266841, -518093464, -370899750, - -739277751, -1536141862, 1323144535, 61311905, - 1997411085, 376844204, 213777604, -217643712, - 9135381, 1625809335, -1490225159, -1342673351, - 1117190829, -57654514, 1825108855, -1281819325, - 1111251351, -1726129724, 1684324211, -1773988491, - 367251975, 810756730, -1941182952, 1175080310 }, + { 0x6afee91fu, 0x7fa31d5bu, 0x38a0b217u, 0x99df9baeu, + 0xfe72991du, 0x727d3c04u, 0x20943f99u, 0xd08e7826u, + 0x69e7c8a2u, 0xdeeccc8eu, 0x6b9af76fu, 0x553311c4u, + 0x07b9e247u, 0x54c8bbcau, 0x6a540d81u, 0x48dbf567u, + 0x98c92877u, 0x134fbfdeu, 0x01b32564u, 0x24581948u, + 0x6cddc3b8u, 0x0cd444dau, 0xfe0381ccu, 0xf15818dfu, + 0xc06e6d42u, 0x2e2f6412u, 0x093a6737u, 0x94d83b31u, + 0xa466c87au, 0xb3f284a0u, 0xa694ec2cu, 0x053359e6u, + 0x9717ee6au, 0x0732e080u, 0x220d5008u, 0xdc4af350u, + 0x93d0a7c3u, 0xe330c9eau, 0xcac3da1eu, 0x8ebecf8fu, + 0xc2be387fu, 0x38a14e89u, 0x211586f0u, 0x18b846f5u, + 0x43be4c72u, 0xb578c204u, 0x1bbfb230u, 0xf1e267a8u, + 0xa2d3e656u, 0x64b8e4feu, 0xe7e83d4bu, 0x3e77a943u, + 0x3559ffd9u, 0x0ebb0f99u, 0x0aa76ce6u, 0xd3786ea7u, + 0xbca8cd6bu, 0x068ca8e8u, 0xeb1de2ffu, 0x3e3ecd6cu, + 0xe0d9d825u, 0xb1edc762u, 0xdec60b24u, 0xd6931904u}, + { 0xccdcb989u, 0xe19281f9u, 0xa6e80accu, 0xb7f40560u, + 0x0efb0bccu, 0x7f12b0bbu, 0x1e90531au, 0x136d95d0u, + 0x9e660665u, 0x7d54918fu, 0xe3b93ea2u, 0x2f415d10u, + 0x3d2df6e6u, 0x7a627ecfu, 0xa6f22d70u, 0xb995907au, + 0x09de16b2u, 0xfeb8bd61u, 0xf24ec294u, 0x716a427fu, + 0x2e12046fu, 0xeaf3d56au, 0xd9b873adu, 0x0ced340bu, + 0xbc9cec09u, 0x73c65903u, 0xee39ce9bu, 0x3eede25au, + 0x397633b7u, 0x2583c165u, 0x8514f97du, 0xe9166510u, + 0x0b6fae99u, 0xa47139fdu, 0xdb8352f0u, 0xb2ad7f2cu, + 0xa11552e2u, 0xd4d490a7u, 0xe11e8568u, 0xe9e484dau, + 0xd3ef8449u, 0xa47055dau, 0x4edd9557u, 0x03a78ba1u, + 0x770e130du, 0x16762facu, 0x0cbdfcc4u, 0xf3070540u, + 0x008b6515u, 0x60e7e1b7u, 0xa72cf7f9u, 0xaff86e39u, + 0x4296faadu, 0xfc90430eu, 0x6cc8f377u, 0xb398fd43u, + 0x423c5997u, 0x991d59c4u, 0x6464bf73u, 0x96431575u, + 0x15e3d207u, 0x30532a7au, 0x8c4be618u, 0x460a4d76u }, 3 }; RSAPublicKey test_f4_key = { 64, 0xc9bd1f21, - { 293133087u, 3210546773u, 865313125u, 250921607u, - 3158780490u, 943703457u, 1242806226u, 2986289859u, - 2942743769u, 2457906415u, 2719374299u, 1783459420u, - 149579627u, 3081531591u, 3440738617u, 2788543742u, - 2758457512u, 1146764939u, 3699497403u, 2446203424u, - 1744968926u, 1159130537u, 2370028300u, 3978231572u, - 3392699980u, 1487782451u, 1180150567u, 2841334302u, - 3753960204u, 961373345u, 3333628321u, 748825784u, - 2978557276u, 1566596926u, 1613056060u, 2600292737u, - 1847226629u, 50398611u, 1890374404u, 2878700735u, - 2286201787u, 1401186359u, 619285059u, 731930817u, - 2340993166u, 1156490245u, 2992241729u, 151498140u, - 318782170u, 3480838990u, 2100383433u, 4223552555u, - 3628927011u, 4247846280u, 1759029513u, 4215632601u, - 2719154626u, 3490334597u, 1751299340u, 3487864726u, - 3668753795u, 4217506054u, 3748782284u, 3150295088u }, - { 1772626313u, 445326068u, 3477676155u, 1758201194u, - 2986784722u, 491035581u, 3922936562u, 702212696u, - 2979856666u, 3324974564u, 2488428922u, 3056318590u, - 1626954946u, 664714029u, 398585816u, 3964097931u, - 3356701905u, 2298377729u, 2040082097u, 3025491477u, - 539143308u, 3348777868u, 2995302452u, 3602465520u, - 212480763u, 2691021393u, 1307177300u, 704008044u, - 2031136606u, 1054106474u, 3838318865u, 2441343869u, - 1477566916u, 700949900u, 2534790355u, 3353533667u, - 336163563u, 4106790558u, 2701448228u, 1571536379u, - 1103842411u, 3623110423u, 1635278839u, 1577828979u, - 910322800u, 715583630u, 138128831u, 1017877531u, - 2289162787u, 447994798u, 1897243165u, 4121561445u, - 4150719842u, 2131821093u, 2262395396u, 3305771534u, - 980753571u, 3256525190u, 3128121808u, 1072869975u, - 3507939515u, 4229109952u, 118381341u, 2209831334u }, + { 0x1178db1fu, 0xbf5d0e55u, 0x3393a165u, 0x0ef4c287u, + 0xbc472a4au, 0x383fc5a1u, 0x4a13b7d2u, 0xb1ff2ac3u, + 0xaf66b4d9u, 0x9280acefu, 0xa2165bdbu, 0x6a4d6e5cu, + 0x08ea676bu, 0xb7ac70c7u, 0xcd158139u, 0xa635ccfeu, + 0xa46ab8a8u, 0x445a3e8bu, 0xdc81d9bbu, 0x91ce1a20u, + 0x68021cdeu, 0x4516eda9u, 0x8d43c30cu, 0xed1eff14u, + 0xca387e4cu, 0x58adc233u, 0x4657ab27u, 0xa95b521eu, + 0xdfc0e30cu, 0x394d64a1u, 0xc6b321a1u, 0x2ca22cb8u, + 0xb1892d5cu, 0x5d605f3eu, 0x6025483cu, 0x9afd5181u, + 0x6e1a7105u, 0x03010593u, 0x70acd304u, 0xab957cbfu, + 0x8844abbbu, 0x53846837u, 0x24e98a43u, 0x2ba060c1u, + 0x8b88b88eu, 0x44eea405u, 0xb259fc41u, 0x0907ad9cu, + 0x13003adau, 0xcf79634eu, 0x7d314ec9u, 0xfbbe4c2bu, + 0xd84d0823u, 0xfd30fd88u, 0x68d8a909u, 0xfb4572d9u, + 0xa21301c2u, 0xd00a4785u, 0x6862b50cu, 0xcfe49796u, + 0xdaacbd83u, 0xfb620906u, 0xdf71e0ccu, 0xbbc5b030u }, + { 0x69a82189u, 0x1a8b22f4u, 0xcf49207bu, 0x68cc056au, + 0xb206b7d2u, 0x1d449bbdu, 0xe9d342f2u, 0x29daea58u, + 0xb19d011au, 0xc62f15e4u, 0x9452697au, 0xb62bb87eu, + 0x60f95cc2u, 0x279ebb2du, 0x17c1efd8u, 0xec47558bu, + 0xc81334d1u, 0x88fe7601u, 0x79992eb1u, 0xb4555615u, + 0x2022ac8cu, 0xc79a4b8cu, 0xb288b034u, 0xd6b942f0u, + 0x0caa32fbu, 0xa065ba51u, 0x4de9f154u, 0x29f64f6cu, + 0x7910af5eu, 0x3ed4636au, 0xe4c81911u, 0x9183f37du, + 0x5811e1c4u, 0x29c7a58cu, 0x9715d4d3u, 0xc7e2dce3u, + 0x140972ebu, 0xf4c8a69eu, 0xa104d424u, 0x5dabbdfbu, + 0x41cb4c6bu, 0xd7f44717u, 0x61785ff7u, 0x5e0bc273u, + 0x36426c70u, 0x2aa6f08eu, 0x083badbfu, 0x3cab941bu, + 0x8871da23u, 0x1ab3dbaeu, 0x7115a21du, 0xf5aa0965u, + 0xf766f562u, 0x7f110225u, 0x86d96a04u, 0xc50a120eu, + 0x3a751ca3u, 0xc21aa186u, 0xba7359d0u, 0x3ff2b257u, + 0xd116e8bbu, 0xfc1318c0u, 0x070e5b1du, 0x83b759a6u }, 65537 }; @@ -136,30 +138,37 @@ ui_print(const char* format, ...) { int main(int argc, char **argv) { if (argc < 2 || argc > 4) { - fprintf(stderr, "Usage: %s [-f4 | -file ] \n", argv[0]); + fprintf(stderr, "Usage: %s [-sha256] [-f4 | -file ] \n", argv[0]); return 2; } - RSAPublicKey* key = &test_key; + Certificate default_cert; + Certificate* cert = &default_cert; + cert->public_key = &test_key; + cert->hash_len = SHA_DIGEST_SIZE; int num_keys = 1; ++argv; + if (strcmp(argv[0], "-sha256") == 0) { + ++argv; + cert->hash_len = SHA256_DIGEST_SIZE; + } if (strcmp(argv[0], "-f4") == 0) { ++argv; - key = &test_f4_key; + cert->public_key = &test_f4_key; } else if (strcmp(argv[0], "-file") == 0) { ++argv; - key = load_keys(argv[0], &num_keys); + cert = load_keys(argv[0], &num_keys); ++argv; } ui = new FakeUI(); - int result = verify_file(*argv, key, num_keys); + int result = verify_file(*argv, cert, num_keys); if (result == VERIFY_SUCCESS) { - printf("SUCCESS\n"); + printf("VERIFIED\n"); return 0; } else if (result == VERIFY_FAILURE) { - printf("FAILURE\n"); + printf("NOT VERIFIED\n"); return 1; } else { printf("bad return value\n"); diff --git a/verifier_test.sh b/verifier_test.sh index 378b0e5ff..65f77f401 100755 --- a/verifier_test.sh +++ b/verifier_test.sh @@ -64,33 +64,39 @@ $ADB push $ANDROID_PRODUCT_OUT/system/bin/verifier_test \ expect_succeed() { testname "$1 (should succeed)" $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip - run_command $WORK_DIR/verifier_test $WORK_DIR/package.zip || fail + shift + run_command $WORK_DIR/verifier_test "$@" $WORK_DIR/package.zip || fail } expect_fail() { testname "$1 (should fail)" $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip - run_command $WORK_DIR/verifier_test $WORK_DIR/package.zip && fail -} - -expect_succeed_f4() { - testname "$1 (should succeed)" - $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip - run_command $WORK_DIR/verifier_test -f4 $WORK_DIR/package.zip || fail -} - -expect_fail_f4() { - testname "$1 (should fail)" - $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip - run_command $WORK_DIR/verifier_test -f4 $WORK_DIR/package.zip && fail + shift + run_command $WORK_DIR/verifier_test "$@" $WORK_DIR/package.zip && fail } +# not signed at all expect_fail unsigned.zip +# signed in the pre-donut way expect_fail jarsigned.zip + +# success cases expect_succeed otasigned.zip -expect_fail_f4 otasigned.zip -expect_succeed_f4 otasigned_f4.zip +expect_succeed otasigned_f4.zip -f4 +expect_succeed otasigned_sha256.zip -sha256 +expect_succeed otasigned_f4_sha256.zip -sha256 -f4 + +# verified against different key +expect_fail otasigned.zip -f4 expect_fail otasigned_f4.zip + +# verified against right key but wrong hash algorithm +expect_fail otasigned.zip -sha256 +expect_fail otasigned_f4.zip -sha256 -f4 +expect_fail otasigned_sha256.zip +expect_fail otasigned_f4_sha256.zip -f4 + +# various other cases expect_fail random.zip expect_fail fake-eocd.zip expect_fail alter-metadata.zip -- cgit v1.2.3 From 58207b84d31c6c281aebf9384024203eadea5316 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 25 Sep 2013 16:41:07 -0700 Subject: fix use of RGBA images in minui Bug: 10934401 Change-Id: I471b30e5b2e877c620e4a0a524e950cddbc6d2c0 --- minui/resources.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/minui/resources.c b/minui/resources.c index c0a9ccacb..b20c00abd 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -123,10 +123,18 @@ 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 = (channels == 3) ? GGL_PIXEL_FORMAT_RGBX_8888 : - ((color_type == PNG_COLOR_TYPE_PALETTE ? GGL_PIXEL_FORMAT_RGBA_8888 : GGL_PIXEL_FORMAT_L_8)); - int alpha = 0; + if (channels == 3) { + surface->format = GGL_PIXEL_FORMAT_RGBX_8888; + } else if (color_type == PNG_COLOR_TYPE_PALETTE) { + surface->format = GGL_PIXEL_FORMAT_RGBA_8888; + } else if (channels == 1) { + surface->format = GGL_PIXEL_FORMAT_L_8; + } else { + surface->format = GGL_PIXEL_FORMAT_RGBA_8888; + } + + int alpha = (channels == 4); if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); } -- cgit v1.2.3 From 80a7a4642be31db7ecd5eaa9e62b78deaa461146 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Fri, 30 Aug 2013 16:59:06 -0700 Subject: screen_ui: Initialize text buffer Zero initialize the text buffer to make recovery not render garbage when showing the menu or messages. Change-Id: I0dd0d357757f6b0fd52ad3b3617d42bb1b835245 --- screen_ui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/screen_ui.cpp b/screen_ui.cpp index 93e260936..b53ab1158 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -86,6 +86,8 @@ ScreenRecoveryUI::ScreenRecoveryUI() : for (int i = 0; i < 5; i++) backgroundIcon[i] = NULL; + memset(text, 0, sizeof(text)); + pthread_mutex_init(&updateMutex, NULL); self = this; } -- cgit v1.2.3 From 7a4adb5268ae71260c86788ccdeb7a699c80ee0a Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Wed, 9 Oct 2013 10:14:35 -0700 Subject: Add support for ECDSA signatures This adds support for key version 5 which is an EC key using the NIST P-256 curve parameters. OTAs may be signed with these keys using the ECDSA signature algorithm with SHA-256. Change-Id: Id88672a3deb70681c78d5ea0d739e10f839e4567 --- Android.mk | 11 +- asn1_decoder.cpp | 190 ++++++++++++++++++++++++++++ asn1_decoder.h | 36 ++++++ testdata/otasigned_ecdsa_sha256.zip | Bin 0 -> 3085 bytes testdata/testkey_ecdsa.pk8 | Bin 0 -> 138 bytes testdata/testkey_ecdsa.x509.pem | 10 ++ tests/Android.mk | 26 ++++ tests/asn1_decoder_test.cpp | 238 ++++++++++++++++++++++++++++++++++++ verifier.cpp | 230 +++++++++++++++++++++++++++++----- verifier.h | 17 ++- verifier_test.cpp | 102 +++++++++++++--- verifier_test.sh | 22 +++- 12 files changed, 822 insertions(+), 60 deletions(-) create mode 100644 asn1_decoder.cpp create mode 100644 asn1_decoder.h create mode 100644 testdata/otasigned_ecdsa_sha256.zip create mode 100644 testdata/testkey_ecdsa.pk8 create mode 100644 testdata/testkey_ecdsa.x509.pem create mode 100644 tests/Android.mk create mode 100644 tests/asn1_decoder_test.cpp diff --git a/Android.mk b/Android.mk index 075fa2cfe..3d6156819 100644 --- a/Android.mk +++ b/Android.mk @@ -24,6 +24,7 @@ LOCAL_SRC_FILES := \ roots.cpp \ ui.cpp \ screen_ui.cpp \ + asn1_decoder.cpp \ verifier.cpp \ adb_install.cpp @@ -76,7 +77,13 @@ LOCAL_C_INCLUDES += system/extras/ext4_utils include $(BUILD_EXECUTABLE) - +# All the APIs for testing +include $(CLEAR_VARS) +LOCAL_MODULE := libverifier +LOCAL_MODULE_TAGS := tests +LOCAL_SRC_FILES := \ + asn1_decoder.cpp +include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := verifier_test @@ -84,6 +91,7 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := \ verifier_test.cpp \ + asn1_decoder.cpp \ verifier.cpp \ ui.cpp LOCAL_STATIC_LIBRARIES := \ @@ -100,6 +108,7 @@ include $(LOCAL_PATH)/minui/Android.mk \ $(LOCAL_PATH)/minzip/Android.mk \ $(LOCAL_PATH)/minadbd/Android.mk \ $(LOCAL_PATH)/mtdutils/Android.mk \ + $(LOCAL_PATH)/tests/Android.mk \ $(LOCAL_PATH)/tools/Android.mk \ $(LOCAL_PATH)/edify/Android.mk \ $(LOCAL_PATH)/updater/Android.mk \ diff --git a/asn1_decoder.cpp b/asn1_decoder.cpp new file mode 100644 index 000000000..7280f7480 --- /dev/null +++ b/asn1_decoder.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2013 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 +#include + +#include "asn1_decoder.h" + + +typedef struct asn1_context { + size_t length; + uint8_t* p; + int app_type; +} asn1_context_t; + + +static const int kMaskConstructed = 0xE0; +static const int kMaskTag = 0x7F; +static const int kMaskAppType = 0x1F; + +static const int kTagOctetString = 0x04; +static const int kTagOid = 0x06; +static const int kTagSequence = 0x30; +static const int kTagSet = 0x31; +static const int kTagConstructed = 0xA0; + +asn1_context_t* asn1_context_new(uint8_t* buffer, size_t length) { + asn1_context_t* ctx = (asn1_context_t*) calloc(1, sizeof(asn1_context_t)); + if (ctx == NULL) { + return NULL; + } + ctx->p = buffer; + ctx->length = length; + return ctx; +} + +void asn1_context_free(asn1_context_t* ctx) { + free(ctx); +} + +static inline int peek_byte(asn1_context_t* ctx) { + if (ctx->length <= 0) { + return -1; + } + return *ctx->p; +} + +static inline int get_byte(asn1_context_t* ctx) { + if (ctx->length <= 0) { + return -1; + } + int byte = *ctx->p; + ctx->p++; + ctx->length--; + return byte; +} + +static inline bool skip_bytes(asn1_context_t* ctx, size_t num_skip) { + if (ctx->length < num_skip) { + return false; + } + ctx->p += num_skip; + ctx->length -= num_skip; + return true; +} + +static bool decode_length(asn1_context_t* ctx, size_t* out_len) { + int num_octets = get_byte(ctx); + if (num_octets == -1) { + return false; + } + if ((num_octets & 0x80) == 0x00) { + *out_len = num_octets; + return 1; + } + num_octets &= kMaskTag; + if ((size_t)num_octets >= sizeof(size_t)) { + return false; + } + size_t length = 0; + for (int i = 0; i < num_octets; ++i) { + int byte = get_byte(ctx); + if (byte == -1) { + return false; + } + length <<= 8; + length += byte; + } + *out_len = length; + return true; +} + +/** + * Returns the constructed type and advances the pointer. E.g. A0 -> 0 + */ +asn1_context_t* asn1_constructed_get(asn1_context_t* ctx) { + int type = get_byte(ctx); + if (type == -1 || (type & kMaskConstructed) != kTagConstructed) { + return NULL; + } + size_t length; + if (!decode_length(ctx, &length) || length > ctx->length) { + return NULL; + } + asn1_context_t* app_ctx = asn1_context_new(ctx->p, length); + app_ctx->app_type = type & kMaskAppType; + return app_ctx; +} + +bool asn1_constructed_skip_all(asn1_context_t* ctx) { + int byte = peek_byte(ctx); + while (byte != -1 && (byte & kMaskConstructed) == kTagConstructed) { + skip_bytes(ctx, 1); + size_t length; + if (!decode_length(ctx, &length) || !skip_bytes(ctx, length)) { + return false; + } + byte = peek_byte(ctx); + } + return byte != -1; +} + +int asn1_constructed_type(asn1_context_t* ctx) { + return ctx->app_type; +} + +asn1_context_t* asn1_sequence_get(asn1_context_t* ctx) { + if ((get_byte(ctx) & kMaskTag) != kTagSequence) { + return NULL; + } + size_t length; + if (!decode_length(ctx, &length) || length > ctx->length) { + return NULL; + } + return asn1_context_new(ctx->p, length); +} + +asn1_context_t* asn1_set_get(asn1_context_t* ctx) { + if ((get_byte(ctx) & kMaskTag) != kTagSet) { + return NULL; + } + size_t length; + if (!decode_length(ctx, &length) || length > ctx->length) { + return NULL; + } + return asn1_context_new(ctx->p, length); +} + +bool asn1_sequence_next(asn1_context_t* ctx) { + size_t length; + if (get_byte(ctx) == -1 || !decode_length(ctx, &length) || !skip_bytes(ctx, length)) { + return false; + } + return true; +} + +bool asn1_oid_get(asn1_context_t* ctx, uint8_t** oid, size_t* length) { + if (get_byte(ctx) != kTagOid) { + return false; + } + if (!decode_length(ctx, length) || *length == 0 || *length > ctx->length) { + return false; + } + *oid = ctx->p; + return true; +} + +bool asn1_octet_string_get(asn1_context_t* ctx, uint8_t** octet_string, size_t* length) { + if (get_byte(ctx) != kTagOctetString) { + return false; + } + if (!decode_length(ctx, length) || *length == 0 || *length > ctx->length) { + return false; + } + *octet_string = ctx->p; + return true; +} diff --git a/asn1_decoder.h b/asn1_decoder.h new file mode 100644 index 000000000..b17141c44 --- /dev/null +++ b/asn1_decoder.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 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 ASN1_DECODER_H_ +#define ASN1_DECODER_H_ + +#include + +typedef struct asn1_context asn1_context_t; + +asn1_context_t* asn1_context_new(uint8_t* buffer, size_t length); +void asn1_context_free(asn1_context_t* ctx); +asn1_context_t* asn1_constructed_get(asn1_context_t* ctx); +bool asn1_constructed_skip_all(asn1_context_t* ctx); +int asn1_constructed_type(asn1_context_t* ctx); +asn1_context_t* asn1_sequence_get(asn1_context_t* ctx); +asn1_context_t* asn1_set_get(asn1_context_t* ctx); +bool asn1_sequence_next(asn1_context_t* seq); +bool asn1_oid_get(asn1_context_t* ctx, uint8_t** oid, size_t* length); +bool asn1_octet_string_get(asn1_context_t* ctx, uint8_t** octet_string, size_t* length); + +#endif /* ASN1_DECODER_H_ */ diff --git a/testdata/otasigned_ecdsa_sha256.zip b/testdata/otasigned_ecdsa_sha256.zip new file mode 100644 index 000000000..999fcdd0f Binary files /dev/null and b/testdata/otasigned_ecdsa_sha256.zip differ diff --git a/testdata/testkey_ecdsa.pk8 b/testdata/testkey_ecdsa.pk8 new file mode 100644 index 000000000..9a521c8cf Binary files /dev/null and b/testdata/testkey_ecdsa.pk8 differ diff --git a/testdata/testkey_ecdsa.x509.pem b/testdata/testkey_ecdsa.x509.pem new file mode 100644 index 000000000..b12283645 --- /dev/null +++ b/testdata/testkey_ecdsa.x509.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBezCCASACCQC4g5wurPSmtzAKBggqhkjOPQQDAjBFMQswCQYDVQQGEwJBVTET +MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMB4XDTEzMTAwODIxMTAxM1oXDTE0MTAwODIxMTAxM1owRTELMAkGA1UE +BhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp +ZGdpdHMgUHR5IEx0ZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGcO1QDowF2E +RboWVmAYI2oXTr5MHAJ4xpMUFsrWVvoktYSN2RhNuOl5jZGvSBsQII9p/4qfjLmS +TBaCfQ0Xmt4wCgYIKoZIzj0EAwIDSQAwRgIhAIJjWmZAwngc2VcHUhYp2oSLoCQ+ +P+7AtbAn5242AqfOAiEAghO0t6jTKs0LUhLJrQwbOkHyZMVdZaG2vcwV9y9H5Qc= +-----END CERTIFICATE----- diff --git a/tests/Android.mk b/tests/Android.mk new file mode 100644 index 000000000..4d99d5249 --- /dev/null +++ b/tests/Android.mk @@ -0,0 +1,26 @@ +# Build the unit tests. +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +# Build the unit tests. +test_src_files := \ + asn1_decoder_test.cpp + +shared_libraries := \ + liblog \ + libcutils + +static_libraries := \ + libgtest \ + libgtest_main \ + libverifier + +$(foreach file,$(test_src_files), \ + $(eval include $(CLEAR_VARS)) \ + $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ + $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ + $(eval LOCAL_SRC_FILES := $(file)) \ + $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ + $(eval LOCAL_C_INCLUDES := $(LOCAL_PATH)/..) \ + $(eval include $(BUILD_NATIVE_TEST)) \ +) \ No newline at end of file diff --git a/tests/asn1_decoder_test.cpp b/tests/asn1_decoder_test.cpp new file mode 100644 index 000000000..af96d87d2 --- /dev/null +++ b/tests/asn1_decoder_test.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2013 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. + */ + +#define LOG_TAG "asn1_decoder_test" + +#include +#include +#include +#include + +#include "asn1_decoder.h" + +namespace android { + +class Asn1DecoderTest : public testing::Test { +}; + +TEST_F(Asn1DecoderTest, Empty_Failure) { + uint8_t empty[] = { }; + asn1_context_t* ctx = asn1_context_new(empty, sizeof(empty)); + + EXPECT_EQ(NULL, asn1_constructed_get(ctx)); + EXPECT_FALSE(asn1_constructed_skip_all(ctx)); + EXPECT_EQ(0, asn1_constructed_type(ctx)); + EXPECT_EQ(NULL, asn1_sequence_get(ctx)); + EXPECT_EQ(NULL, asn1_set_get(ctx)); + EXPECT_FALSE(asn1_sequence_next(ctx)); + + uint8_t* junk; + size_t length; + EXPECT_FALSE(asn1_oid_get(ctx, &junk, &length)); + EXPECT_FALSE(asn1_octet_string_get(ctx, &junk, &length)); + + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, ConstructedGet_TruncatedLength_Failure) { + uint8_t truncated[] = { 0xA0, 0x82, }; + asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated)); + EXPECT_EQ(NULL, asn1_constructed_get(ctx)); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, ConstructedGet_LengthTooBig_Failure) { + uint8_t truncated[] = { 0xA0, 0x8a, 0xA5, 0x5A, 0xA5, 0x5A, + 0xA5, 0x5A, 0xA5, 0x5A, 0xA5, 0x5A, }; + asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated)); + EXPECT_EQ(NULL, asn1_constructed_get(ctx)); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, ConstructedGet_TooSmallForChild_Failure) { + uint8_t data[] = { 0xA5, 0x02, 0x06, 0x01, 0x01, }; + asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); + asn1_context_t* ptr = asn1_constructed_get(ctx); + ASSERT_NE((asn1_context_t*)NULL, ptr); + EXPECT_EQ(5, asn1_constructed_type(ptr)); + uint8_t* oid; + size_t length; + EXPECT_FALSE(asn1_oid_get(ptr, &oid, &length)); + asn1_context_free(ptr); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, ConstructedGet_Success) { + uint8_t data[] = { 0xA5, 0x03, 0x06, 0x01, 0x01, }; + asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); + asn1_context_t* ptr = asn1_constructed_get(ctx); + ASSERT_NE((asn1_context_t*)NULL, ptr); + EXPECT_EQ(5, asn1_constructed_type(ptr)); + uint8_t* oid; + size_t length; + ASSERT_TRUE(asn1_oid_get(ptr, &oid, &length)); + EXPECT_EQ(1U, length); + EXPECT_EQ(0x01U, *oid); + asn1_context_free(ptr); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, ConstructedSkipAll_TruncatedLength_Failure) { + uint8_t truncated[] = { 0xA2, 0x82, }; + asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated)); + EXPECT_FALSE(asn1_constructed_skip_all(ctx)); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, ConstructedSkipAll_Success) { + uint8_t data[] = { 0xA0, 0x03, 0x02, 0x01, 0x01, + 0xA1, 0x03, 0x02, 0x01, 0x01, + 0x06, 0x01, 0xA5, }; + asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); + ASSERT_TRUE(asn1_constructed_skip_all(ctx)); + uint8_t* oid; + size_t length; + ASSERT_TRUE(asn1_oid_get(ctx, &oid, &length)); + EXPECT_EQ(1U, length); + EXPECT_EQ(0xA5U, *oid); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, SequenceGet_TruncatedLength_Failure) { + uint8_t truncated[] = { 0x30, 0x82, }; + asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated)); + EXPECT_EQ(NULL, asn1_sequence_get(ctx)); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, SequenceGet_TooSmallForChild_Failure) { + uint8_t data[] = { 0x30, 0x02, 0x06, 0x01, 0x01, }; + asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); + asn1_context_t* ptr = asn1_sequence_get(ctx); + ASSERT_NE((asn1_context_t*)NULL, ptr); + uint8_t* oid; + size_t length; + EXPECT_FALSE(asn1_oid_get(ptr, &oid, &length)); + asn1_context_free(ptr); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, SequenceGet_Success) { + uint8_t data[] = { 0x30, 0x03, 0x06, 0x01, 0x01, }; + asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); + asn1_context_t* ptr = asn1_sequence_get(ctx); + ASSERT_NE((asn1_context_t*)NULL, ptr); + uint8_t* oid; + size_t length; + ASSERT_TRUE(asn1_oid_get(ptr, &oid, &length)); + EXPECT_EQ(1U, length); + EXPECT_EQ(0x01U, *oid); + asn1_context_free(ptr); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, SetGet_TruncatedLength_Failure) { + uint8_t truncated[] = { 0x31, 0x82, }; + asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated)); + EXPECT_EQ(NULL, asn1_set_get(ctx)); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, SetGet_TooSmallForChild_Failure) { + uint8_t data[] = { 0x31, 0x02, 0x06, 0x01, 0x01, }; + asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); + asn1_context_t* ptr = asn1_set_get(ctx); + ASSERT_NE((asn1_context_t*)NULL, ptr); + uint8_t* oid; + size_t length; + EXPECT_FALSE(asn1_oid_get(ptr, &oid, &length)); + asn1_context_free(ptr); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, SetGet_Success) { + uint8_t data[] = { 0x31, 0x03, 0x06, 0x01, 0xBA, }; + asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); + asn1_context_t* ptr = asn1_set_get(ctx); + ASSERT_NE((asn1_context_t*)NULL, ptr); + uint8_t* oid; + size_t length; + ASSERT_TRUE(asn1_oid_get(ptr, &oid, &length)); + EXPECT_EQ(1U, length); + EXPECT_EQ(0xBAU, *oid); + asn1_context_free(ptr); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, OidGet_LengthZero_Failure) { + uint8_t data[] = { 0x06, 0x00, 0x01, }; + asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); + uint8_t* oid; + size_t length; + EXPECT_FALSE(asn1_oid_get(ctx, &oid, &length)); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, OidGet_TooSmall_Failure) { + uint8_t data[] = { 0x06, 0x01, }; + asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); + uint8_t* oid; + size_t length; + EXPECT_FALSE(asn1_oid_get(ctx, &oid, &length)); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, OidGet_Success) { + uint8_t data[] = { 0x06, 0x01, 0x99, }; + asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); + uint8_t* oid; + size_t length; + ASSERT_TRUE(asn1_oid_get(ctx, &oid, &length)); + EXPECT_EQ(1U, length); + EXPECT_EQ(0x99U, *oid); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, OctetStringGet_LengthZero_Failure) { + uint8_t data[] = { 0x04, 0x00, 0x55, }; + asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); + uint8_t* string; + size_t length; + ASSERT_FALSE(asn1_octet_string_get(ctx, &string, &length)); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, OctetStringGet_TooSmall_Failure) { + uint8_t data[] = { 0x04, 0x01, }; + asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); + uint8_t* string; + size_t length; + ASSERT_FALSE(asn1_octet_string_get(ctx, &string, &length)); + asn1_context_free(ctx); +} + +TEST_F(Asn1DecoderTest, OctetStringGet_Success) { + uint8_t data[] = { 0x04, 0x01, 0xAA, }; + asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); + uint8_t* string; + size_t length; + ASSERT_TRUE(asn1_octet_string_get(ctx, &string, &length)); + EXPECT_EQ(1U, length); + EXPECT_EQ(0xAAU, *string); + asn1_context_free(ctx); +} + +} // namespace android diff --git a/verifier.cpp b/verifier.cpp index 782a83863..0930fbd15 100644 --- a/verifier.cpp +++ b/verifier.cpp @@ -14,10 +14,14 @@ * limitations under the License. */ +#include "asn1_decoder.h" #include "common.h" -#include "verifier.h" #include "ui.h" +#include "verifier.h" +#include "mincrypt/dsa_sig.h" +#include "mincrypt/p256.h" +#include "mincrypt/p256_ecdsa.h" #include "mincrypt/rsa.h" #include "mincrypt/sha.h" #include "mincrypt/sha256.h" @@ -28,6 +32,78 @@ extern RecoveryUI* ui; +/* + * Simple version of PKCS#7 SignedData extraction. This extracts the + * signature OCTET STRING to be used for signature verification. + * + * For full details, see http://www.ietf.org/rfc/rfc3852.txt + * + * The PKCS#7 structure looks like: + * + * SEQUENCE (ContentInfo) + * OID (ContentType) + * [0] (content) + * SEQUENCE (SignedData) + * INTEGER (version CMSVersion) + * SET (DigestAlgorithmIdentifiers) + * SEQUENCE (EncapsulatedContentInfo) + * [0] (CertificateSet OPTIONAL) + * [1] (RevocationInfoChoices OPTIONAL) + * SET (SignerInfos) + * SEQUENCE (SignerInfo) + * INTEGER (CMSVersion) + * SEQUENCE (SignerIdentifier) + * SEQUENCE (DigestAlgorithmIdentifier) + * SEQUENCE (SignatureAlgorithmIdentifier) + * OCTET STRING (SignatureValue) + */ +static bool read_pkcs7(uint8_t* pkcs7_der, size_t pkcs7_der_len, uint8_t** sig_der, + size_t* sig_der_length) { + asn1_context_t* ctx = asn1_context_new(pkcs7_der, pkcs7_der_len); + if (ctx == NULL) { + return false; + } + + asn1_context_t* pkcs7_seq = asn1_sequence_get(ctx); + if (pkcs7_seq != NULL && asn1_sequence_next(pkcs7_seq)) { + asn1_context_t *signed_data_app = asn1_constructed_get(pkcs7_seq); + if (signed_data_app != NULL) { + asn1_context_t* signed_data_seq = asn1_sequence_get(signed_data_app); + if (signed_data_seq != NULL + && asn1_sequence_next(signed_data_seq) + && asn1_sequence_next(signed_data_seq) + && asn1_sequence_next(signed_data_seq) + && asn1_constructed_skip_all(signed_data_seq)) { + asn1_context_t *sig_set = asn1_set_get(signed_data_seq); + if (sig_set != NULL) { + asn1_context_t* sig_seq = asn1_sequence_get(sig_set); + if (sig_seq != NULL + && asn1_sequence_next(sig_seq) + && asn1_sequence_next(sig_seq) + && asn1_sequence_next(sig_seq) + && asn1_sequence_next(sig_seq)) { + uint8_t* sig_der_ptr; + if (asn1_octet_string_get(sig_seq, &sig_der_ptr, sig_der_length)) { + *sig_der = (uint8_t*) malloc(*sig_der_length); + if (*sig_der != NULL) { + memcpy(*sig_der, sig_der_ptr, *sig_der_length); + } + } + asn1_context_free(sig_seq); + } + asn1_context_free(sig_set); + } + asn1_context_free(signed_data_seq); + } + asn1_context_free(signed_data_app); + } + asn1_context_free(pkcs7_seq); + } + asn1_context_free(ctx); + + return *sig_der != 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. @@ -79,9 +155,8 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys LOGI("comment is %d bytes; signature %d bytes from end\n", comment_size, signature_start); - if (signature_start - FOOTER_SIZE < RSANUMBYTES) { - // "signature" block isn't big enough to contain an RSA block. - LOGE("signature is too short\n"); + if (signature_start <= FOOTER_SIZE) { + LOGE("Signature start is in the footer"); fclose(f); return VERIFY_FAILURE; } @@ -187,6 +262,23 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys const uint8_t* sha1 = SHA_final(&sha1_ctx); const uint8_t* sha256 = SHA256_final(&sha256_ctx); + uint8_t* sig_der = NULL; + size_t sig_der_length = 0; + + size_t signature_size = signature_start - FOOTER_SIZE; + if (!read_pkcs7(eocd + eocd_size - signature_start, signature_size, &sig_der, + &sig_der_length)) { + LOGE("Could not find signature DER block\n"); + free(eocd); + return VERIFY_FAILURE; + } + free(eocd); + + /* + * Check to make sure at least one of the keys matches the signature. Since + * any key can match, we need to try each before determining a verification + * failure has happened. + */ for (i = 0; i < numKeys; ++i) { const uint8_t* hash; switch (pKeys[i].hash_len) { @@ -197,16 +289,46 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that // the signing tool appends after the signature itself. - if (RSA_verify(pKeys[i].public_key, eocd + eocd_size - 6 - RSANUMBYTES, - RSANUMBYTES, hash, pKeys[i].hash_len)) { - LOGI("whole-file signature verified against key %d\n", i); - free(eocd); + if (pKeys[i].key_type == Certificate::RSA) { + if (sig_der_length < RSANUMBYTES) { + // "signature" block isn't big enough to contain an RSA block. + LOGI("signature is too short for RSA key %d\n", i); + continue; + } + + if (!RSA_verify(pKeys[i].rsa, sig_der, RSANUMBYTES, + hash, pKeys[i].hash_len)) { + LOGI("failed to verify against RSA key %d\n", i); + continue; + } + + LOGI("whole-file signature verified against RSA key %d\n", i); + free(sig_der); + return VERIFY_SUCCESS; + } else if (pKeys[i].key_type == Certificate::EC + && pKeys[i].hash_len == SHA256_DIGEST_SIZE) { + p256_int r, s; + if (!dsa_sig_unpack(sig_der, sig_der_length, &r, &s)) { + LOGI("Not a DSA signature block for EC key %d\n", i); + continue; + } + + p256_int p256_hash; + p256_from_bin(hash, &p256_hash); + if (!p256_ecdsa_verify(&(pKeys[i].ec->x), &(pKeys[i].ec->y), + &p256_hash, &r, &s)) { + LOGI("failed to verify against EC key %d\n", i); + continue; + } + + LOGI("whole-file signature verified against EC key %d\n", i); + free(sig_der); return VERIFY_SUCCESS; } else { - LOGI("failed to verify against key %d\n", i); + LOGI("Unknown key type %d\n", pKeys[i].key_type); } } - free(eocd); + free(sig_der); LOGE("failed to verify whole-file signature\n"); return VERIFY_FAILURE; } @@ -238,6 +360,7 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys // 2: 2048-bit RSA key with e=65537 and SHA-1 hash // 3: 2048-bit RSA key with e=3 and SHA-256 hash // 4: 2048-bit RSA key with e=65537 and SHA-256 hash +// 5: 256-bit EC key using the NIST P-256 curve parameters and SHA-256 hash // // Returns NULL if the file failed to parse, or if it contain zero keys. Certificate* @@ -258,28 +381,41 @@ load_keys(const char* filename, int* numKeys) { ++*numKeys; out = (Certificate*)realloc(out, *numKeys * sizeof(Certificate)); Certificate* cert = out + (*numKeys - 1); - cert->public_key = (RSAPublicKey*)malloc(sizeof(RSAPublicKey)); + memset(cert, '\0', sizeof(Certificate)); char start_char; if (fscanf(f, " %c", &start_char) != 1) goto exit; if (start_char == '{') { // a version 1 key has no version specifier. - cert->public_key->exponent = 3; + cert->key_type = Certificate::RSA; + cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey)); + cert->rsa->exponent = 3; cert->hash_len = SHA_DIGEST_SIZE; } else if (start_char == 'v') { int version; if (fscanf(f, "%d {", &version) != 1) goto exit; switch (version) { case 2: - cert->public_key->exponent = 65537; + cert->key_type = Certificate::RSA; + cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey)); + cert->rsa->exponent = 65537; cert->hash_len = SHA_DIGEST_SIZE; break; case 3: - cert->public_key->exponent = 3; + cert->key_type = Certificate::RSA; + cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey)); + cert->rsa->exponent = 3; cert->hash_len = SHA256_DIGEST_SIZE; break; case 4: - cert->public_key->exponent = 65537; + cert->key_type = Certificate::RSA; + cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey)); + cert->rsa->exponent = 65537; + cert->hash_len = SHA256_DIGEST_SIZE; + break; + case 5: + cert->key_type = Certificate::EC; + cert->ec = (ECPublicKey*)calloc(1, sizeof(ECPublicKey)); cert->hash_len = SHA256_DIGEST_SIZE; break; default: @@ -287,23 +423,55 @@ load_keys(const char* filename, int* numKeys) { } } - RSAPublicKey* key = cert->public_key; - if (fscanf(f, " %i , 0x%x , { %u", - &(key->len), &(key->n0inv), &(key->n[0])) != 3) { - goto exit; - } - if (key->len != RSANUMWORDS) { - LOGE("key length (%d) does not match expected size\n", key->len); + if (cert->key_type == Certificate::RSA) { + RSAPublicKey* key = cert->rsa; + if (fscanf(f, " %i , 0x%x , { %u", + &(key->len), &(key->n0inv), &(key->n[0])) != 3) { + goto exit; + } + if (key->len != RSANUMWORDS) { + LOGE("key length (%d) does not match expected size\n", key->len); + goto exit; + } + for (i = 1; i < key->len; ++i) { + if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit; + } + if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit; + for (i = 1; i < key->len; ++i) { + if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit; + } + fscanf(f, " } } "); + + LOGI("read key e=%d hash=%d\n", key->exponent, cert->hash_len); + } else if (cert->key_type == Certificate::EC) { + ECPublicKey* key = cert->ec; + int key_len; + unsigned int byte; + uint8_t x_bytes[P256_NBYTES]; + uint8_t y_bytes[P256_NBYTES]; + if (fscanf(f, " %i , { %u", &key_len, &byte) != 2) goto exit; + if (key_len != P256_NBYTES) { + LOGE("Key length (%d) does not match expected size %d\n", key_len, P256_NBYTES); + goto exit; + } + x_bytes[P256_NBYTES - 1] = byte; + for (i = P256_NBYTES - 2; i >= 0; --i) { + if (fscanf(f, " , %u", &byte) != 1) goto exit; + x_bytes[i] = byte; + } + if (fscanf(f, " } , { %u", &byte) != 1) goto exit; + y_bytes[P256_NBYTES - 1] = byte; + for (i = P256_NBYTES - 2; i >= 0; --i) { + if (fscanf(f, " , %u", &byte) != 1) goto exit; + y_bytes[i] = byte; + } + fscanf(f, " } } "); + p256_from_bin(x_bytes, &key->x); + p256_from_bin(y_bytes, &key->y); + } else { + LOGE("Unknown key type %d\n", cert->key_type); goto exit; } - for (i = 1; i < key->len; ++i) { - if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit; - } - if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit; - for (i = 1; i < key->len; ++i) { - if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit; - } - fscanf(f, " } } "); // if the line ends in a comma, this file has more keys. switch (fgetc(f)) { @@ -319,8 +487,6 @@ load_keys(const char* filename, int* numKeys) { LOGE("unexpected character between keys\n"); goto exit; } - - LOGI("read key e=%d hash=%d\n", key->exponent, cert->hash_len); } } diff --git a/verifier.h b/verifier.h index 6ce1b44d1..023d3bf89 100644 --- a/verifier.h +++ b/verifier.h @@ -17,11 +17,24 @@ #ifndef _RECOVERY_VERIFIER_H #define _RECOVERY_VERIFIER_H +#include "mincrypt/p256.h" #include "mincrypt/rsa.h" -typedef struct Certificate { +typedef struct { + p256_int x; + p256_int y; +} ECPublicKey; + +typedef struct { + typedef enum { + RSA, + EC, + } KeyType; + int hash_len; // SHA_DIGEST_SIZE (SHA-1) or SHA256_DIGEST_SIZE (SHA-256) - RSAPublicKey* public_key; + KeyType key_type; + RSAPublicKey* rsa; + ECPublicKey* ec; } Certificate; /* Look in the file for a signature footer, and verify that it diff --git a/verifier_test.cpp b/verifier_test.cpp index 1063cbae5..88fcad4ea 100644 --- a/verifier_test.cpp +++ b/verifier_test.cpp @@ -100,6 +100,18 @@ RSAPublicKey test_f4_key = 65537 }; +ECPublicKey test_ec_key = + { + { + {0xd656fa24u, 0x931416cau, 0x1c0278c6u, 0x174ebe4cu, + 0x6018236au, 0x45ba1656u, 0xe8c05d84u, 0x670ed500u} + }, + { + {0x0d179adeu, 0x4c16827du, 0x9f8cb992u, 0x8f69ff8au, + 0x481b1020u, 0x798d91afu, 0x184db8e9u, 0xb5848dd9u} + } + }; + RecoveryUI* ui = NULL; // verifier expects to find a UI object; we provide one that does @@ -136,34 +148,86 @@ ui_print(const char* format, ...) { va_end(ap); } +static Certificate* add_certificate(Certificate** certsp, int* num_keys, + Certificate::KeyType key_type) { + int i = *num_keys; + *num_keys = *num_keys + 1; + *certsp = (Certificate*) realloc(*certsp, *num_keys * sizeof(Certificate)); + Certificate* certs = *certsp; + certs[i].rsa = NULL; + certs[i].ec = NULL; + certs[i].key_type = key_type; + certs[i].hash_len = SHA_DIGEST_SIZE; + return &certs[i]; +} + int main(int argc, char **argv) { - if (argc < 2 || argc > 4) { - fprintf(stderr, "Usage: %s [-sha256] [-f4 | -file ] \n", argv[0]); + if (argc < 2) { + fprintf(stderr, "Usage: %s [-sha256] [-ec | -f4 | -file ] \n", argv[0]); return 2; } + Certificate* certs = NULL; + int num_keys = 0; - Certificate default_cert; - Certificate* cert = &default_cert; - cert->public_key = &test_key; - cert->hash_len = SHA_DIGEST_SIZE; - int num_keys = 1; - ++argv; - if (strcmp(argv[0], "-sha256") == 0) { - ++argv; - cert->hash_len = SHA256_DIGEST_SIZE; + int argn = 1; + while (argn < argc) { + if (strcmp(argv[argn], "-sha256") == 0) { + if (num_keys == 0) { + fprintf(stderr, "May only specify -sha256 after key type\n"); + return 2; + } + ++argn; + Certificate* cert = &certs[num_keys - 1]; + cert->hash_len = SHA256_DIGEST_SIZE; + } else if (strcmp(argv[argn], "-ec") == 0) { + ++argn; + Certificate* cert = add_certificate(&certs, &num_keys, Certificate::EC); + cert->ec = &test_ec_key; + } else if (strcmp(argv[argn], "-e3") == 0) { + ++argn; + Certificate* cert = add_certificate(&certs, &num_keys, Certificate::RSA); + cert->rsa = &test_key; + } else if (strcmp(argv[argn], "-f4") == 0) { + ++argn; + Certificate* cert = add_certificate(&certs, &num_keys, Certificate::RSA); + cert->rsa = &test_f4_key; + } else if (strcmp(argv[argn], "-file") == 0) { + if (certs != NULL) { + fprintf(stderr, "Cannot specify -file with other certs specified\n"); + return 2; + } + ++argn; + certs = load_keys(argv[argn], &num_keys); + ++argn; + } else if (argv[argn][0] == '-') { + fprintf(stderr, "Unknown argument %s\n", argv[argn]); + return 2; + } else { + break; + } } - if (strcmp(argv[0], "-f4") == 0) { - ++argv; - cert->public_key = &test_f4_key; - } else if (strcmp(argv[0], "-file") == 0) { - ++argv; - cert = load_keys(argv[0], &num_keys); - ++argv; + + if (argn == argc) { + fprintf(stderr, "Must specify package to verify\n"); + return 2; + } + + if (num_keys == 0) { + certs = (Certificate*) calloc(1, sizeof(Certificate)); + if (certs == NULL) { + fprintf(stderr, "Failure allocating memory for default certificate\n"); + return 1; + } + certs->key_type = Certificate::RSA; + certs->rsa = &test_key; + certs->ec = NULL; + certs->hash_len = SHA_DIGEST_SIZE; + num_keys = 1; } ui = new FakeUI(); - int result = verify_file(*argv, cert, num_keys); + int result = verify_file(argv[argn], certs, num_keys); if (result == VERIFY_SUCCESS) { printf("VERIFIED\n"); return 0; diff --git a/verifier_test.sh b/verifier_test.sh index 65f77f401..4761cef4a 100755 --- a/verifier_test.sh +++ b/verifier_test.sh @@ -81,20 +81,30 @@ expect_fail unsigned.zip expect_fail jarsigned.zip # success cases -expect_succeed otasigned.zip +expect_succeed otasigned.zip -e3 expect_succeed otasigned_f4.zip -f4 -expect_succeed otasigned_sha256.zip -sha256 -expect_succeed otasigned_f4_sha256.zip -sha256 -f4 +expect_succeed otasigned_sha256.zip -e3 -sha256 +expect_succeed otasigned_f4_sha256.zip -f4 -sha256 +expect_succeed otasigned_ecdsa_sha256.zip -ec -sha256 + +# success with multiple keys +expect_succeed otasigned.zip -f4 -e3 +expect_succeed otasigned_f4.zip -ec -f4 +expect_succeed otasigned_sha256.zip -ec -e3 -e3 -sha256 +expect_succeed otasigned_f4_sha256.zip -ec -sha256 -e3 -f4 -sha256 +expect_succeed otasigned_ecdsa_sha256.zip -f4 -sha256 -e3 -ec -sha256 # verified against different key expect_fail otasigned.zip -f4 -expect_fail otasigned_f4.zip +expect_fail otasigned_f4.zip -e3 +expect_fail otasigned_ecdsa_sha256.zip -e3 -sha256 # verified against right key but wrong hash algorithm -expect_fail otasigned.zip -sha256 -expect_fail otasigned_f4.zip -sha256 -f4 +expect_fail otasigned.zip -e3 -sha256 +expect_fail otasigned_f4.zip -f4 -sha256 expect_fail otasigned_sha256.zip expect_fail otasigned_f4_sha256.zip -f4 +expect_fail otasigned_ecdsa_sha256.zip # various other cases expect_fail random.zip -- cgit v1.2.3 From ce7ca7165bb263c91ca7616a21457032c645e23d Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Wed, 6 Nov 2013 17:42:20 -0800 Subject: Enable incremental builder to find files that moved, and try to process them via patch + rename, instead of delete + add. b/11437930 Change-Id: I984349fbc9a8dac4379e00c0d66fc7d22c4eb834 --- updater/install.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/updater/install.c b/updater/install.c index 0a859452a..9fb1b63d7 100644 --- a/updater/install.c +++ b/updater/install.c @@ -285,6 +285,40 @@ done: return StringValue(result); } +Value* RenameFn(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* src_name; + char* dst_name; + + if (ReadArgs(state, argv, 2, &src_name, &dst_name) < 0) { + return NULL; + } + if (strlen(src_name) == 0) { + ErrorAbort(state, "src_name argument to %s() can't be empty", name); + goto done; + } + if (strlen(dst_name) == 0) { + ErrorAbort(state, "dst_name argument to %s() can't be empty", + name); + goto done; + } + + if (rename(src_name, dst_name) != 0) { + ErrorAbort(state, "Rename of %s() to %s() failed, error %s()", + src_name, dst_name, strerror(errno)); + } else { + result = dst_name; + } + +done: + free(src_name); + if (result != dst_name) free(dst_name); + return StringValue(result); +} Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) { char** paths = malloc(argc * sizeof(char*)); @@ -1425,6 +1459,7 @@ void RegisterInstallFunctions() { RegisterFunction("read_file", ReadFileFn); RegisterFunction("sha1_check", Sha1CheckFn); + RegisterFunction("rename", RenameFn); RegisterFunction("wipe_cache", WipeCacheFn); -- cgit v1.2.3 From 9b8ae8038be65c2ec236bc04590716fbcd5363f6 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Wed, 17 Jul 2013 10:34:36 -0700 Subject: Fix rare crash seen when dereferencing backgroundIcon[NONE]. Because backgroundIcon[] is not initialized by the ScreenRecoveryUI constructor, it should be initialized explicitly to NULL in Init(). If it is not initialized, ScreenRecoveryUI::SetBackground() can fail for the NONE icon because the NULL test can fail and junk will be dereferenced. Change-Id: I4e3738d2e241ed90df43c984fb41e0072933f50a --- screen_ui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/screen_ui.cpp b/screen_ui.cpp index 8376341c3..be7abca1d 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -376,6 +376,7 @@ void ScreenRecoveryUI::Init() text_cols = gr_fb_width() / char_width; if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1; + backgroundIcon[NONE] = NULL; LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]); backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE]; LoadBitmap("icon_error", &backgroundIcon[ERROR]); -- cgit v1.2.3 From 027429a34fd229d7546640bd5b629156da8dd0fd Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Wed, 17 Jul 2013 10:41:49 -0700 Subject: Restore default umask after forking for update-binary. A system/core change made in Mar 26 2012 6ebf12f "init: Change umask of forked processes to 077" changed the default umask of services forked from init. Because recovery is forked from init, it has a umask of 077. Therefore when update-binary is forked from recovery, it too has a umask of 077. This umask is overly restrictive and can cause problems for scripts relying on minzip to extract binaries directly into the target filesystem. Any directories updated by minzip will have their permissions reset to r-x------ and created files will have similarly restrictive permissions. As it seems unlikely this security measure was intended to have this side effect on legacy sideloads that do not have chmods to repair the damage done by minzip, this change reverts the umask to 022 in the fork made for update-binary. Change-Id: Ib1a3fc83aa4ecc7480b5d0c00f3c7d0d040d4887 --- install.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/install.cpp b/install.cpp index 797a525fd..980830cc0 100644 --- a/install.cpp +++ b/install.cpp @@ -120,6 +120,7 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { pid_t pid = fork(); if (pid == 0) { + umask(022); close(pipefd[0]); execv(binary, (char* const*)args); fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno)); -- cgit v1.2.3 From a0ecc8c4277c5c216915ee60b927aea972c95b25 Mon Sep 17 00:00:00 2001 From: synergy dev Date: Wed, 6 Nov 2013 16:31:54 -0800 Subject: minui: do not use flexible array initialization Avoiding the use of gnu extensions improves code portability Change-Id: I441a49ce08b6ecb1d735ca453552fa92026c5016 --- minui/font_10x18.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minui/font_10x18.h b/minui/font_10x18.h index 7f96465cc..29d705344 100644 --- a/minui/font_10x18.h +++ b/minui/font_10x18.h @@ -3,7 +3,7 @@ struct { unsigned height; unsigned cwidth; unsigned cheight; - unsigned char rundata[]; + unsigned char rundata[2973]; } font = { .width = 960, .height = 18, -- cgit v1.2.3 From c87bab101893e8322b49d7c8600e3367b20ab50a Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 25 Nov 2013 13:53:25 -0800 Subject: add the functions for multi-stage packages to updater In order to support multi-stage recovery packages, we add the set_stage() and get_stage() functions, which store a short string somewhere it can be accessed across invocations of recovery. We also add reboot_now() which updater can invoke to immediately reboot the device, without doing normal recovery cleanup. (It can also choose whether to boot off the boot or recovery partition.) If the stage string is of the form "#/#", recovery's UI will be augmented with a simple indicator of what stage you're in, so it doesn't look like a reboot loop. Change-Id: I62f7ff0bc802b549c9bcf3cc154a6bad99f94603 --- bootloader.h | 15 ++++++- recovery.cpp | 18 ++++++++ res/images/stage_empty.png | Bin 0 -> 322 bytes res/images/stage_fill.png | Bin 0 -> 258 bytes screen_ui.cpp | 36 +++++++++++++-- screen_ui.h | 12 +++-- ui.h | 2 + updater/install.c | 107 ++++++++++++++++++++++++++++++++++++++++++++- 8 files changed, 181 insertions(+), 9 deletions(-) create mode 100644 res/images/stage_empty.png create mode 100644 res/images/stage_fill.png diff --git a/bootloader.h b/bootloader.h index 712aa1a2d..c2895dd91 100644 --- a/bootloader.h +++ b/bootloader.h @@ -38,11 +38,24 @@ extern "C" { * The recovery field is only written by linux and used * for the system to send a message to recovery or the * other way around. + * + * The stage field is written by packages which restart themselves + * multiple times, so that the UI can reflect which invocation of the + * package it is. If the value is of the format "#/#" (eg, "1/3"), + * the UI will add a simple indicator of that status. */ struct bootloader_message { char command[32]; char status[32]; - char recovery[1024]; + char recovery[768]; + + // The 'recovery' field used to be 1024 bytes. It has only ever + // been used to store the recovery command line, so 768 bytes + // should be plenty. We carve off the last 256 bytes to store the + // stage string (for multistage packages) and possible future + // expansion. + char stage[32]; + char reserved[224]; }; /* Read and write the bootloader command from the "misc" partition. diff --git a/recovery.cpp b/recovery.cpp index d803cadf1..43cd9dafe 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -56,6 +56,7 @@ static const struct option OPTIONS[] = { { "show_text", no_argument, NULL, 't' }, { "just_exit", no_argument, NULL, 'x' }, { "locale", required_argument, NULL, 'l' }, + { "stages", required_argument, NULL, 'g' }, { NULL, 0, NULL, 0 }, }; @@ -76,6 +77,7 @@ static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload"; RecoveryUI* ui = NULL; char* locale = NULL; char recovery_version[PROPERTY_VALUE_MAX+1]; +char* stage = NULL; /* * The recovery tool communicates with the main system through /cache files. @@ -172,6 +174,7 @@ get_args(int *argc, char ***argv) { struct bootloader_message boot; memset(&boot, 0, sizeof(boot)); get_bootloader_message(&boot); // this may fail, leaving a zeroed structure + stage = strndup(boot.stage, sizeof(boot.stage)); if (boot.command[0] != 0 && boot.command[0] != 255) { LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command); @@ -959,6 +962,14 @@ main(int argc, char **argv) { case 't': show_text = 1; break; case 'x': just_exit = true; break; case 'l': locale = optarg; break; + case 'g': { + if (stage == NULL || *stage == '\0') { + char buffer[20] = "1/"; + strncat(buffer, optarg, sizeof(buffer)-3); + stage = strdup(buffer); + } + break; + } case '?': LOGE("Invalid command argument\n"); continue; @@ -969,12 +980,19 @@ main(int argc, char **argv) { load_locale_from_cache(); } printf("locale is [%s]\n", locale); + printf("stage is [%s]\n", stage, stage); Device* device = make_device(); ui = device->GetUI(); gCurrentUI = ui; ui->Init(); + + int st_cur, st_max; + if (stage != NULL && sscanf(stage, "%d/%d", &st_cur, &st_max) == 2) { + ui->SetStage(st_cur, st_max); + } + ui->SetLocale(locale); ui->SetBackground(RecoveryUI::NONE); if (show_text) ui->ShowText(true); diff --git a/res/images/stage_empty.png b/res/images/stage_empty.png new file mode 100644 index 000000000..251ec1969 Binary files /dev/null and b/res/images/stage_empty.png differ diff --git a/res/images/stage_fill.png b/res/images/stage_fill.png new file mode 100644 index 000000000..1ab79e862 Binary files /dev/null and b/res/images/stage_fill.png differ diff --git a/screen_ui.cpp b/screen_ui.cpp index 8376341c3..27d0a245c 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -81,7 +81,9 @@ ScreenRecoveryUI::ScreenRecoveryUI() : install_overlay_offset_x(13), install_overlay_offset_y(190), overlay_offset_x(-1), - overlay_offset_y(-1) { + overlay_offset_y(-1), + stage(-1), + max_stage(-1) { for (int i = 0; i < 5; i++) backgroundIcon[i] = NULL; @@ -120,14 +122,28 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) int iconHeight = gr_get_height(surface); int textWidth = gr_get_width(text_surface); int textHeight = gr_get_height(text_surface); + int stageHeight = gr_get_height(stageMarkerEmpty); + + int sh = (max_stage >= 0) ? stageHeight : 0; int iconX = (gr_fb_width() - iconWidth) / 2; - int iconY = (gr_fb_height() - (iconHeight+textHeight+40)) / 2; + int iconY = (gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2; int textX = (gr_fb_width() - textWidth) / 2; - int textY = ((gr_fb_height() - (iconHeight+textHeight+40)) / 2) + iconHeight + 40; + int textY = ((gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2) + iconHeight + 40; gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY); + if (stageHeight > 0) { + int sw = gr_get_width(stageMarkerEmpty); + int x = (gr_fb_width() - max_stage * gr_get_width(stageMarkerEmpty)) / 2; + int y = iconY + iconHeight + 20; + for (int i = 0; i < max_stage; ++i) { + gr_blit((i < stage) ? stageMarkerFill : stageMarkerEmpty, + 0, 0, sw, stageHeight, x, y); + x += sw; + } + } + if (icon == INSTALLING_UPDATE || icon == ERASING) { draw_install_overlay_locked(installingFrame); } @@ -383,6 +399,8 @@ void ScreenRecoveryUI::Init() LoadBitmap("progress_empty", &progressBarEmpty); LoadBitmap("progress_fill", &progressBarFill); + LoadBitmap("stage_empty", &stageMarkerEmpty); + LoadBitmap("stage_fill", &stageMarkerFill); LoadLocalizedBitmap("installing_text", &backgroundText[INSTALLING_UPDATE]); LoadLocalizedBitmap("erasing_text", &backgroundText[ERASING]); @@ -453,7 +471,10 @@ void ScreenRecoveryUI::SetBackground(Icon icon) gr_surface text = backgroundText[icon]; overlay_offset_x = install_overlay_offset_x + (gr_fb_width() - gr_get_width(bg)) / 2; overlay_offset_y = install_overlay_offset_y + - (gr_fb_height() - (gr_get_height(bg) + gr_get_height(text) + 40)) / 2; + (gr_fb_height() - (gr_get_height(bg) + + gr_get_height(text) + + 40 + + ((max_stage >= 0) ? gr_get_height(stageMarkerEmpty) : 0))) / 2; } currentIcon = icon; @@ -505,6 +526,13 @@ void ScreenRecoveryUI::SetProgress(float fraction) pthread_mutex_unlock(&updateMutex); } +void ScreenRecoveryUI::SetStage(int current, int max) { + pthread_mutex_lock(&updateMutex); + stage = current; + max_stage = max; + pthread_mutex_unlock(&updateMutex); +} + void ScreenRecoveryUI::Print(const char *fmt, ...) { char buf[256]; diff --git a/screen_ui.h b/screen_ui.h index fc35d95b6..5c4366d28 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -39,6 +39,8 @@ class ScreenRecoveryUI : public RecoveryUI { void ShowProgress(float portion, float seconds); void SetProgress(float fraction); + void SetStage(int current, int max); + // text log void ShowText(bool visible); bool IsTextVisible(); @@ -58,9 +60,6 @@ class ScreenRecoveryUI : public RecoveryUI { enum UIElement { HEADER, MENU, MENU_SEL_BG, MENU_SEL_FG, LOG, TEXT_FILL }; virtual void SetColor(UIElement e); - protected: - int install_overlay_offset_x, install_overlay_offset_y; - private: Icon currentIcon; int installingFrame; @@ -73,6 +72,8 @@ class ScreenRecoveryUI : public RecoveryUI { gr_surface *progressBarIndeterminate; gr_surface progressBarEmpty; gr_surface progressBarFill; + gr_surface stageMarkerEmpty; + gr_surface stageMarkerFill; ProgressType progressBarType; @@ -102,8 +103,13 @@ class ScreenRecoveryUI : public RecoveryUI { int animation_fps; int indeterminate_frames; int installing_frames; + protected: + int install_overlay_offset_x, install_overlay_offset_y; + private: int overlay_offset_x, overlay_offset_y; + int stage, max_stage; + void draw_install_overlay_locked(int frame); void draw_background_locked(Icon icon); void draw_progress_locked(); diff --git a/ui.h b/ui.h index 6c8987a33..0757260b7 100644 --- a/ui.h +++ b/ui.h @@ -30,6 +30,8 @@ class RecoveryUI { // Initialize the object; called before anything else. virtual void Init(); + // Show a stage indicator. Call immediately after Init(). + virtual void SetStage(int current, int max) { } // After calling Init(), you can tell the UI what locale it is operating in. virtual void SetLocale(const char* locale) { } diff --git a/updater/install.c b/updater/install.c index 9fb1b63d7..9f299f6cc 100644 --- a/updater/install.c +++ b/updater/install.c @@ -34,6 +34,9 @@ #include #include +#include "bootloader.h" +#include "applypatch/applypatch.h" +#include "cutils/android_reboot.h" #include "cutils/misc.h" #include "cutils/properties.h" #include "edify/expr.h" @@ -42,7 +45,6 @@ #include "mtdutils/mounts.h" #include "mtdutils/mtdutils.h" #include "updater.h" -#include "applypatch/applypatch.h" #ifdef USE_EXT4 #include "make_ext4fs.h" @@ -1419,6 +1421,105 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) { return v; } +// Immediately reboot the device. Recovery is not finished normally, +// so if you reboot into recovery it will re-start applying the +// current package (because nothing has cleared the copy of the +// arguments stored in the BCB). +// +// The argument is the partition name passed to the android reboot +// property. It can be "recovery" to boot from the recovery +// partition, or "" (empty string) to boot from the regular boot +// partition. +Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + + char* filename; + char* property; + if (ReadArgs(state, argv, 2, &filename, &property) < 0) return NULL; + + char buffer[80]; + + // zero out the 'command' field of the bootloader message. + memset(buffer, 0, sizeof(((struct bootloader_message*)0)->command)); + FILE* f = fopen(filename, "r+b"); + fseek(f, offsetof(struct bootloader_message, command), SEEK_SET); + fwrite(buffer, sizeof(((struct bootloader_message*)0)->command), 1, f); + fclose(f); + free(filename); + + strcpy(buffer, "reboot,"); + if (property != NULL) { + strncat(buffer, property, sizeof(buffer)-10); + } + + property_set(ANDROID_RB_PROPERTY, buffer); + + sleep(5); + free(property); + ErrorAbort(state, "%s() failed to reboot", name); + return NULL; +} + +// Store a string value somewhere that future invocations of recovery +// can access it. This value is called the "stage" and can be used to +// drive packages that need to do reboots in the middle of +// installation and keep track of where they are in the multi-stage +// install. +// +// The first argument is the block device for the misc partition +// ("/misc" in the fstab), which is where this value is stored. The +// second argument is the string to store; it should not exceed 31 +// bytes. +Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + + char* filename; + char* stagestr; + if (ReadArgs(state, argv, 2, &filename, &stagestr) < 0) return NULL; + + // Store this value in the misc partition, immediately after the + // bootloader message that the main recovery uses to save its + // arguments in case of the device restarting midway through + // package installation. + FILE* f = fopen(filename, "r+b"); + fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET); + int to_write = strlen(stagestr)+1; + int max_size = sizeof(((struct bootloader_message*)0)->stage); + if (to_write > max_size) { + to_write = max_size; + stagestr[max_size-1] = 0; + } + fwrite(stagestr, to_write, 1, f); + fclose(f); + + free(stagestr); + return StringValue(filename); +} + +// Return the value most recently saved with SetStageFn. The argument +// is the block device for the misc partition. +Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + } + + char* filename; + if (ReadArgs(state, argv, 1, &filename) < 0) return NULL; + + char buffer[sizeof(((struct bootloader_message*)0)->stage)]; + FILE* f = fopen(filename, "rb"); + fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET); + fread(buffer, sizeof(buffer), 1, f); + fclose(f); + buffer[sizeof(buffer)-1] = '\0'; + + return StringValue(strdup(buffer)); +} + void RegisterInstallFunctions() { RegisterFunction("mount", MountFn); RegisterFunction("is_mounted", IsMountedFn); @@ -1466,4 +1567,8 @@ void RegisterInstallFunctions() { RegisterFunction("ui_print", UIPrintFn); RegisterFunction("run_program", RunProgramFn); + + RegisterFunction("reboot_now", RebootNowFn); + RegisterFunction("get_stage", GetStageFn); + RegisterFunction("set_stage", SetStageFn); } -- cgit v1.2.3 From f39989a36d4724a4852e28b9dca3d372d3f7873d Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 11 Dec 2013 15:40:28 -0800 Subject: recovery: wipe encryption metadata along with data partition This assumes that the metadata is correctly defined in fstab. Which apparently some devices don't do. Bug: 8766487 Bug: 12112624 Change-Id: I1b14b9d4c888e9348527984be3dce04bdd9f4de0 --- Android.mk | 2 +- roots.cpp | 27 ++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Android.mk b/Android.mk index 1308066df..c544db964 100644 --- a/Android.mk +++ b/Android.mk @@ -57,7 +57,7 @@ LOCAL_STATIC_LIBRARIES := \ ifeq ($(TARGET_USERIMAGES_USE_EXT4), true) LOCAL_CFLAGS += -DUSE_EXT4 - LOCAL_C_INCLUDES += system/extras/ext4_utils + LOCAL_C_INCLUDES += system/extras/ext4_utils system/vold LOCAL_STATIC_LIBRARIES += libext4_utils_static libz endif diff --git a/roots.cpp b/roots.cpp index 113dba1bd..47cea0bec 100644 --- a/roots.cpp +++ b/roots.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "mtdutils/mtdutils.h" @@ -28,6 +29,10 @@ #include "roots.h" #include "common.h" #include "make_ext4fs.h" +extern "C" { +#include "wipe.h" +#include "cryptfs.h" +} static struct fstab *fstab = NULL; @@ -191,11 +196,31 @@ int format_volume(const char* volume) { } if (strcmp(v->fs_type, "ext4") == 0) { - int result = make_ext4fs(v->blk_device, v->length, volume, sehandle); + ssize_t length = 0; + if (v->length != 0) { + length = v->length; + } else if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0) { + length = -CRYPT_FOOTER_OFFSET; + } + int result = make_ext4fs(v->blk_device, length, volume, sehandle); if (result != 0) { LOGE("format_volume: make_extf4fs failed on %s\n", v->blk_device); return -1; } + + // if there's a key_loc that looks like a path, it should be a + // block device for storing encryption metadata. wipe it too. + if (v->key_loc != NULL && v->key_loc[0] == '/') { + LOGI("wiping %s\n", v->key_loc); + int fd = open(v->key_loc, O_WRONLY | O_CREAT, 0644); + if (fd < 0) { + LOGE("format_volume: failed to open %s\n", v->key_loc); + return -1; + } + wipe_block_device(fd, get_file_size(fd)); + close(fd); + } + return 0; } -- cgit v1.2.3 From 6eed2247714967eb0ce913d7da782fa884a4c1bd Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Fri, 13 Dec 2013 17:13:11 -0800 Subject: Don't abort on read_file if the file is missing. Change-Id: I85726bf736203d602428114145c3b98692580656 --- updater/install.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/updater/install.c b/updater/install.c index 9f299f6cc..b6852b0c7 100644 --- a/updater/install.c +++ b/updater/install.c @@ -1353,7 +1353,6 @@ Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) { } if (args[0]->size < 0) { - printf("%s(): no file contents received", name); return StringValue(strdup("")); } uint8_t digest[SHA_DIGEST_SIZE]; @@ -1406,12 +1405,11 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) { FileContents fc; if (LoadFileContents(filename, &fc, RETOUCH_DONT_MASK) != 0) { - ErrorAbort(state, "%s() loading \"%s\" failed: %s", - name, filename, strerror(errno)); free(filename); - free(v); + v->size = -1; + v->data = NULL; free(fc.data); - return NULL; + return v; } v->size = fc.size; -- cgit v1.2.3 From 168724c31ad5241e157ebb35135a734fa075d53b Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 19 Dec 2013 15:16:57 -0800 Subject: fix unnecessarily slow writing of EMMC partitions These were attempts to write partitions "conservatively" in hopes of fixing the problems with writing the radio partition on Nexus 4. They didn't work (a kernel patch was needed), but got left in. They make writing of partitions unnecessarily slow (ie, we really shouldn't need to sync() after every 4kb). Roll back most of them, but leave the verification read-back in. Change-Id: I94badc0979e88816c5aa0485f6316c02be69173c --- applypatch/applypatch.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 6b8da2a8a..cb9bc2349 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -424,20 +424,18 @@ int WriteToPartition(unsigned char* data, size_t len, { size_t start = 0; int success = 0; - int fd = open(partition, O_RDWR | O_SYNC); + int fd = open(partition, O_RDWR); if (fd < 0) { printf("failed to open %s: %s\n", partition, strerror(errno)); return -1; } int attempt; - for (attempt = 0; attempt < 10; ++attempt) { - size_t next_sync = start + (1<<20); - printf("raw O_SYNC write %s attempt %d start at %d\n", partition, attempt+1, start); + for (attempt = 0; attempt < 2; ++attempt) { lseek(fd, start, SEEK_SET); while (start < len) { size_t to_write = len - start; - if (to_write > 4096) to_write = 4096; + if (to_write > 1<<20) to_write = 1<<20; ssize_t written = write(fd, data+start, to_write); if (written < 0) { @@ -450,10 +448,6 @@ int WriteToPartition(unsigned char* data, size_t len, } } start += written; - if (start >= next_sync) { - fsync(fd); - next_sync = start + (1<<20); - } } fsync(fd); @@ -506,8 +500,6 @@ int WriteToPartition(unsigned char* data, size_t len, success = true; break; } - - sleep(2); } if (!success) { @@ -519,11 +511,7 @@ int WriteToPartition(unsigned char* data, size_t len, printf("error closing %s (%s)\n", partition, strerror(errno)); return -1; } - // hack: sync and sleep after closing in hopes of getting - // the data actually onto flash. - printf("sleeping after close\n"); sync(); - sleep(5); break; } } -- cgit v1.2.3 From 2768efdf9fe67d179f6cb733e6541f04b6f2bf46 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 13 Jan 2014 10:11:20 -0800 Subject: remove dead code from minzip minzip had some features that were used when reading APKs, but APK handling now uses libziparchive instead of minzip. Remove these unused functions. Change-Id: Iead89209a716bfe9e3d339bf85b3e97e33a41f35 --- minzip/SysUtil.c | 117 ------------------------------------------------------- minzip/SysUtil.h | 17 -------- 2 files changed, 134 deletions(-) diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c index 31c76d6d4..e6f650644 100644 --- a/minzip/SysUtil.c +++ b/minzip/SysUtil.c @@ -16,34 +16,6 @@ #include "Log.h" #include "SysUtil.h" -/* - * Having trouble finding a portable way to get this. sysconf(_SC_PAGE_SIZE) - * seems appropriate, but we don't have that on the device. Some systems - * have getpagesize(2), though the linux man page has some odd cautions. - */ -#define DEFAULT_PAGE_SIZE 4096 - - -/* - * Create an anonymous shared memory segment large enough to hold "length" - * bytes. The actual segment may be larger because mmap() operates on - * page boundaries (usually 4K). - */ -static void* sysCreateAnonShmem(size_t length) -{ - void* ptr; - - ptr = mmap(NULL, length, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANON, -1, 0); - if (ptr == MAP_FAILED) { - LOGW("mmap(%d, RW, SHARED|ANON) failed: %s\n", (int) length, - strerror(errno)); - return NULL; - } - - return ptr; -} - static int getFileStartAndLength(int fd, off_t *start_, size_t *length_) { off_t start, end; @@ -73,41 +45,6 @@ static int getFileStartAndLength(int fd, off_t *start_, size_t *length_) return 0; } -/* - * Pull the contents of a file into an new shared memory segment. We grab - * everything from fd's current offset on. - * - * We need to know the length ahead of time so we can allocate a segment - * of sufficient size. - */ -int sysLoadFileInShmem(int fd, MemMapping* pMap) -{ - off_t start; - size_t length, actual; - void* memPtr; - - assert(pMap != NULL); - - if (getFileStartAndLength(fd, &start, &length) < 0) - return -1; - - memPtr = sysCreateAnonShmem(length); - if (memPtr == NULL) - return -1; - - pMap->baseAddr = pMap->addr = memPtr; - pMap->baseLength = pMap->length = length; - - actual = TEMP_FAILURE_RETRY(read(fd, memPtr, length)); - if (actual != length) { - LOGE("only read %d of %d bytes\n", (int) actual, (int) length); - sysReleaseShmem(pMap); - return -1; - } - - return 0; -} - /* * Map a file (from fd's current offset) into a shared, read-only memory * segment. The file offset must be a multiple of the page size. @@ -139,59 +76,6 @@ int sysMapFileInShmem(int fd, MemMapping* pMap) return 0; } -/* - * Map part of a file (from fd's current offset) into a shared, read-only - * memory segment. - * - * On success, returns 0 and fills out "pMap". On failure, returns a nonzero - * value and does not disturb "pMap". - */ -int sysMapFileSegmentInShmem(int fd, off_t start, long length, - MemMapping* pMap) -{ - off_t dummy; - size_t fileLength, actualLength; - off_t actualStart; - int adjust; - void* memPtr; - - assert(pMap != NULL); - - if (getFileStartAndLength(fd, &dummy, &fileLength) < 0) - return -1; - - if (start + length > (long)fileLength) { - LOGW("bad segment: st=%d len=%ld flen=%d\n", - (int) start, length, (int) fileLength); - return -1; - } - - /* adjust to be page-aligned */ - adjust = start % DEFAULT_PAGE_SIZE; - actualStart = start - adjust; - actualLength = length + adjust; - - memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED, - fd, actualStart); - if (memPtr == MAP_FAILED) { - LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n", - (int) actualLength, fd, (int) actualStart, strerror(errno)); - return -1; - } - - pMap->baseAddr = memPtr; - pMap->baseLength = actualLength; - pMap->addr = (char*)memPtr + adjust; - pMap->length = length; - - LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n", - (int) start, (int) length, - pMap->baseAddr, (int) pMap->baseLength, - pMap->addr, (int) pMap->length); - - return 0; -} - /* * Release a memory mapping. */ @@ -209,4 +93,3 @@ void sysReleaseShmem(MemMapping* pMap) pMap->baseLength = 0; } } - diff --git a/minzip/SysUtil.h b/minzip/SysUtil.h index ec3a4bcfb..55cd0e769 100644 --- a/minzip/SysUtil.h +++ b/minzip/SysUtil.h @@ -26,17 +26,6 @@ INLINE void sysCopyMap(MemMapping* dst, const MemMapping* src) { *dst = *src; } -/* - * Load a file into a new shared memory segment. All data from the current - * offset to the end of the file is pulled in. - * - * The segment is read-write, allowing VM fixups. (It should be modified - * to support .gz/.zip compressed data.) - * - * On success, "pMap" is filled in, and zero is returned. - */ -int sysLoadFileInShmem(int fd, MemMapping* pMap); - /* * Map a file (from fd's current offset) into a shared, * read-only memory segment. @@ -45,12 +34,6 @@ int sysLoadFileInShmem(int fd, MemMapping* pMap); */ int sysMapFileInShmem(int fd, MemMapping* pMap); -/* - * Like sysMapFileInShmem, but on only part of a file. - */ -int sysMapFileSegmentInShmem(int fd, off_t start, long length, - MemMapping* pMap); - /* * Release the pages associated with a shared memory segment. * -- cgit v1.2.3 From 2c9d5b2839307987812db8a939d88272b865bacc Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Mon, 13 Jan 2014 09:44:42 -0500 Subject: Set SELinux security contexts correctly for init and services. Otherwise everything is left running in the kernel domain when booting recovery. Change-Id: Ie3d86547d5be0b68dd1875a97afe1e00fc3e4da1 Signed-off-by: Stephen Smalley --- etc/init.rc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/etc/init.rc b/etc/init.rc index 175489066..5f9ce80a2 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -1,6 +1,13 @@ import /init.recovery.${ro.hardware}.rc on early-init + # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls. + write /sys/fs/selinux/checkreqprot 0 + + # Set the security context for the init process. + # This should occur before anything else (e.g. ueventd) is started. + setcon u:r:init:s0 + start ueventd start healthd @@ -43,15 +50,19 @@ on property:sys.powerctl=* service ueventd /sbin/ueventd critical + seclabel u:r:ueventd:s0 service healthd /sbin/healthd -n critical + seclabel u:r:healthd:s0 service recovery /sbin/recovery + seclabel u:r:recovery:s0 service adbd /sbin/adbd recovery disabled socket adbd stream 660 system system + seclabel u:r:adbd:s0 # Always start adbd on userdebug and eng builds on property:ro.debuggable=1 -- cgit v1.2.3 From 075ef327d494ea1ce07eb038fcc367fb78b14500 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 14 Jan 2014 09:50:35 -0800 Subject: correctly mount tmpfs as /tmp in recovery The syntax of init's mount command changed in April 2008 but recovery's init.rc was never updated, so recovery's /tmp has been on the root fs all this time. Fix. Also add /system/bin to the PATH in recovery, which is handy for debugging. Change-Id: I39f7ae435a8ce3bad691e4b7c307db0bd8de1302 --- etc/init.rc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/init.rc b/etc/init.rc index 5f9ce80a2..8d49f24e6 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -12,7 +12,7 @@ on early-init start healthd on init - export PATH /sbin + export PATH /sbin:/system/bin export ANDROID_ROOT /system export ANDROID_DATA /data export EXTERNAL_STORAGE /sdcard @@ -23,7 +23,7 @@ on init mkdir /system mkdir /data mkdir /cache - mount /tmp /tmp tmpfs + mount tmpfs tmpfs /tmp chown root shell /tmp chmod 0775 /tmp -- cgit v1.2.3 From 99916f0496cfe37891d40f21a9a0e387620a8a60 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 13 Jan 2014 14:16:58 -0800 Subject: do verification and extraction on memory, not files Changes minzip and recovery's file signature verification to work on memory regions, rather than files. For packages which are regular files, install.cpp now mmap()s them into memory and then passes the mapped memory to the verifier and to the minzip library. Support for files which are raw block maps (which will be used when we have packages written to encrypted data partitions) is present but largely untested so far. Bug: 12188746 Change-Id: I12cc3e809834745a489dd9d4ceb558cbccdc3f71 --- Android.mk | 1 + install.cpp | 30 ++++++++--- minzip/SysUtil.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++------- minzip/SysUtil.h | 38 ++++++++------ minzip/Zip.c | 121 +++++++++---------------------------------- minzip/Zip.h | 12 ++--- roots.cpp | 10 +++- updater/updater.c | 15 ++++-- verifier.cpp | 70 ++++++------------------- verifier.h | 9 ++-- verifier_test.cpp | 12 ++++- 11 files changed, 261 insertions(+), 209 deletions(-) diff --git a/Android.mk b/Android.mk index c544db964..bd8d09b80 100644 --- a/Android.mk +++ b/Android.mk @@ -98,6 +98,7 @@ LOCAL_SRC_FILES := \ LOCAL_STATIC_LIBRARIES := \ libmincrypt \ libminui \ + libminzip \ libcutils \ libstdc++ \ libc diff --git a/install.cpp b/install.cpp index 980830cc0..0bd7945c4 100644 --- a/install.cpp +++ b/install.cpp @@ -186,12 +186,22 @@ really_install_package(const char *path, int* wipe_cache) ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME); LOGI("Update location: %s\n", path); - if (ensure_path_mounted(path) != 0) { - LOGE("Can't mount %s\n", path); - return INSTALL_CORRUPT; + // Map the update package into memory. + ui->Print("Opening update package...\n"); + + if (path) { + if (path[0] == '@') { + ensure_path_mounted(path+1); + } else { + ensure_path_mounted(path); + } } - ui->Print("Opening update package...\n"); + MemMapping map; + if (sysMapFile(path, &map) != 0) { + LOGE("failed to map file\n"); + return INSTALL_CORRUPT; + } int numKeys; Certificate* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); @@ -204,27 +214,33 @@ really_install_package(const char *path, int* wipe_cache) ui->Print("Verifying update package...\n"); int err; - err = verify_file(path, loadedKeys, numKeys); + err = verify_file(map.addr, map.length, loadedKeys, numKeys); free(loadedKeys); LOGI("verify_file returned %d\n", err); if (err != VERIFY_SUCCESS) { LOGE("signature verification failed\n"); + sysReleaseMap(&map); return INSTALL_CORRUPT; } /* Try to open the package. */ ZipArchive zip; - err = mzOpenZipArchive(path, &zip); + err = mzOpenZipArchive(map.addr, map.length, &zip); if (err != 0) { LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad"); + sysReleaseMap(&map); return INSTALL_CORRUPT; } /* Verify and install the contents of the package. */ ui->Print("Installing update...\n"); - return try_update_binary(path, &zip, wipe_cache); + int result = try_update_binary(path, &zip, wipe_cache); + + sysReleaseMap(&map); + + return result; } int diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c index e6f650644..2cfa39ae3 100644 --- a/minzip/SysUtil.c +++ b/minzip/SysUtil.c @@ -8,11 +8,14 @@ #include #include #include +#include +#include +#include #include #include #include -#define LOG_TAG "minzip" +#define LOG_TAG "sysutil" #include "Log.h" #include "SysUtil.h" @@ -46,13 +49,13 @@ static int getFileStartAndLength(int fd, off_t *start_, size_t *length_) } /* - * Map a file (from fd's current offset) into a shared, read-only memory + * Map a file (from fd's current offset) into a private, read-only memory * segment. The file offset must be a multiple of the page size. * * On success, returns 0 and fills out "pMap". On failure, returns a nonzero * value and does not disturb "pMap". */ -int sysMapFileInShmem(int fd, MemMapping* pMap) +static int sysMapFD(int fd, MemMapping* pMap) { off_t start; size_t length; @@ -63,33 +66,146 @@ int sysMapFileInShmem(int fd, MemMapping* pMap) if (getFileStartAndLength(fd, &start, &length) < 0) return -1; - memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start); + memPtr = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, start); if (memPtr == MAP_FAILED) { - LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n", (int) length, + LOGW("mmap(%d, R, PRIVATE, %d, %d) failed: %s\n", (int) length, fd, (int) start, strerror(errno)); return -1; } - pMap->baseAddr = pMap->addr = memPtr; - pMap->baseLength = pMap->length = length; + pMap->addr = memPtr; + pMap->length = length; + pMap->range_count = 1; + pMap->ranges = malloc(sizeof(MappedRange)); + pMap->ranges[0].addr = memPtr; + pMap->ranges[0].length = length; return 0; } +static int sysMapBlockFile(FILE* mapf, MemMapping* pMap) +{ + char block_dev[PATH_MAX+1]; + size_t size; + unsigned int blksize; + unsigned int blocks; + unsigned int range_count; + unsigned int i; + + if (fgets(block_dev, sizeof(block_dev), mapf) == NULL) { + LOGW("failed to read block device from header\n"); + return -1; + } + for (i = 0; i < sizeof(block_dev); ++i) { + if (block_dev[i] == '\n') { + block_dev[i] = 0; + break; + } + } + + if (fscanf(mapf, "%d %d\n%d\n", &size, &blksize, &range_count) != 3) { + LOGW("failed to parse block map header\n"); + return -1; + } + + blocks = ((size-1) / blksize) + 1; + + pMap->range_count = range_count; + pMap->ranges = malloc(range_count * sizeof(MappedRange)); + memset(pMap->ranges, 0, range_count * sizeof(MappedRange)); + + // Reserve enough contiguous address space for the whole file. + unsigned char* reserve; + reserve = mmap64(NULL, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (reserve == MAP_FAILED) { + LOGW("failed to reserve address space: %s\n", strerror(errno)); + return -1; + } + + pMap->ranges[range_count-1].addr = reserve; + pMap->ranges[range_count-1].length = blocks * blksize; + + int fd = open(block_dev, O_RDONLY); + if (fd < 0) { + LOGW("failed to open block device %s: %s\n", block_dev, strerror(errno)); + return -1; + } + + unsigned char* next = reserve; + for (i = 0; i < range_count; ++i) { + int start, end; + if (fscanf(mapf, "%d %d\n", &start, &end) != 2) { + LOGW("failed to parse range %d in block map\n", i); + return -1; + } + + void* addr = mmap64(next, (end-start)*blksize, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)start)*blksize); + if (addr == MAP_FAILED) { + LOGW("failed to map block %d: %s\n", i, strerror(errno)); + return -1; + } + pMap->ranges[i].addr = addr; + pMap->ranges[i].length = (end-start)*blksize; + + next += pMap->ranges[i].length; + } + + pMap->addr = reserve; + pMap->length = size; + + return 0; +} + +int sysMapFile(const char* fn, MemMapping* pMap) +{ + memset(pMap, 0, sizeof(*pMap)); + + if (fn && fn[0] == '@') { + // A map of blocks + FILE* mapf = fopen(fn+1, "r"); + if (mapf == NULL) { + LOGV("Unable to open '%s': %s\n", fn+1, strerror(errno)); + return -1; + } + + if (sysMapBlockFile(mapf, pMap) != 0) { + LOGW("Map of '%s' failed\n", fn); + return -1; + } + + fclose(mapf); + } else { + // This is a regular file. + int fd = open(fn, O_RDONLY, 0); + if (fd < 0) { + LOGE("Unable to open '%s': %s\n", fn, strerror(errno)); + return -1; + } + + if (sysMapFD(fd, pMap) != 0) { + LOGE("Map of '%s' failed\n", fn); + close(fd); + return -1; + } + + close(fd); + } + return 0; +} + /* * Release a memory mapping. */ -void sysReleaseShmem(MemMapping* pMap) +void sysReleaseMap(MemMapping* pMap) { - if (pMap->baseAddr == NULL && pMap->baseLength == 0) - return; - - if (munmap(pMap->baseAddr, pMap->baseLength) < 0) { - LOGW("munmap(%p, %d) failed: %s\n", - pMap->baseAddr, (int)pMap->baseLength, strerror(errno)); - } else { - LOGV("munmap(%p, %d) succeeded\n", pMap->baseAddr, pMap->baseLength); - pMap->baseAddr = NULL; - pMap->baseLength = 0; + int i; + for (i = 0; i < pMap->range_count; ++i) { + if (munmap(pMap->ranges[i].addr, pMap->ranges[i].length) < 0) { + LOGW("munmap(%p, %d) failed: %s\n", + pMap->ranges[i].addr, (int)pMap->ranges[i].length, strerror(errno)); + } } + free(pMap->ranges); + pMap->ranges = NULL; + pMap->range_count = 0; } diff --git a/minzip/SysUtil.h b/minzip/SysUtil.h index 55cd0e769..7adff1e54 100644 --- a/minzip/SysUtil.h +++ b/minzip/SysUtil.h @@ -6,39 +6,47 @@ #ifndef _MINZIP_SYSUTIL #define _MINZIP_SYSUTIL -#include "inline_magic.h" - +#include #include +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MappedRange { + void* addr; + size_t length; +} MappedRange; + /* * Use this to keep track of mapped segments. */ typedef struct MemMapping { - void* addr; /* start of data */ - size_t length; /* length of data */ + unsigned char* addr; /* start of data */ + size_t length; /* length of data */ - void* baseAddr; /* page-aligned base address */ - size_t baseLength; /* length of mapping */ + int range_count; + MappedRange* ranges; } MemMapping; -/* copy a map */ -INLINE void sysCopyMap(MemMapping* dst, const MemMapping* src) { - *dst = *src; -} - /* - * Map a file (from fd's current offset) into a shared, - * read-only memory segment. + * Map a file into a private, read-only memory segment. If 'fn' + * begins with an '@' character, it is a map of blocks to be mapped, + * otherwise it is treated as an ordinary file. * * On success, "pMap" is filled in, and zero is returned. */ -int sysMapFileInShmem(int fd, MemMapping* pMap); +int sysMapFile(const char* fn, MemMapping* pMap); /* * Release the pages associated with a shared memory segment. * * This does not free "pMap"; it just releases the memory. */ -void sysReleaseShmem(MemMapping* pMap); +void sysReleaseMap(MemMapping* pMap); + +#ifdef __cplusplus +} +#endif #endif /*_MINZIP_SYSUTIL*/ diff --git a/minzip/Zip.c b/minzip/Zip.c index 439e5d9cd..6752bced5 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -184,7 +184,7 @@ static int validFilename(const char *fileName, unsigned int fileNameLen) * * Returns "true" on success. */ -static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap) +static bool parseZipArchive(ZipArchive* pArchive) { bool result = false; const unsigned char* ptr; @@ -196,7 +196,7 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap) * signature for the first file (LOCSIG) or, if the archive doesn't * have any files in it, the end-of-central-directory signature (ENDSIG). */ - val = get4LE(pMap->addr); + val = get4LE(pArchive->addr); if (val == ENDSIG) { LOGI("Found Zip archive, but it looks empty\n"); goto bail; @@ -209,14 +209,14 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap) * Find the EOCD. We'll find it immediately unless they have a file * comment. */ - ptr = pMap->addr + pMap->length - ENDHDR; + ptr = pArchive->addr + pArchive->length - ENDHDR; - while (ptr >= (const unsigned char*) pMap->addr) { + while (ptr >= (const unsigned char*) pArchive->addr) { if (*ptr == (ENDSIG & 0xff) && get4LE(ptr) == ENDSIG) break; ptr--; } - if (ptr < (const unsigned char*) pMap->addr) { + if (ptr < (const unsigned char*) pArchive->addr) { LOGI("Could not find end-of-central-directory in Zip\n"); goto bail; } @@ -230,9 +230,9 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap) cdOffset = get4LE(ptr + ENDOFF); LOGVV("numEntries=%d cdOffset=%d\n", numEntries, cdOffset); - if (numEntries == 0 || cdOffset >= pMap->length) { + if (numEntries == 0 || cdOffset >= pArchive->length) { LOGW("Invalid entries=%d offset=%d (len=%zd)\n", - numEntries, cdOffset, pMap->length); + numEntries, cdOffset, pArchive->length); goto bail; } @@ -245,14 +245,14 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap) if (pArchive->pEntries == NULL || pArchive->pHash == NULL) goto bail; - ptr = pMap->addr + cdOffset; + ptr = pArchive->addr + cdOffset; for (i = 0; i < numEntries; i++) { ZipEntry* pEntry; unsigned int fileNameLen, extraLen, commentLen, localHdrOffset; const unsigned char* localHdr; const char *fileName; - if (ptr + CENHDR > (const unsigned char*)pMap->addr + pMap->length) { + if (ptr + CENHDR > (const unsigned char*)pArchive->addr + pArchive->length) { LOGW("Ran off the end (at %d)\n", i); goto bail; } @@ -266,7 +266,7 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap) extraLen = get2LE(ptr + CENEXT); commentLen = get2LE(ptr + CENCOM); fileName = (const char*)ptr + CENHDR; - if (fileName + fileNameLen > (const char*)pMap->addr + pMap->length) { + if (fileName + fileNameLen > (const char*)pArchive->addr + pArchive->length) { LOGW("Filename ran off the end (at %d)\n", i); goto bail; } @@ -352,15 +352,15 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap) } pEntry->externalFileAttributes = get4LE(ptr + CENATX); - // Perform pMap->addr + localHdrOffset, ensuring that it won't + // Perform pArchive->addr + localHdrOffset, ensuring that it won't // overflow. This is needed because localHdrOffset is untrusted. - if (!safe_add((uintptr_t *)&localHdr, (uintptr_t)pMap->addr, + if (!safe_add((uintptr_t *)&localHdr, (uintptr_t)pArchive->addr, (uintptr_t)localHdrOffset)) { LOGW("Integer overflow adding in parseZipArchive\n"); goto bail; } if ((uintptr_t)localHdr + LOCHDR > - (uintptr_t)pMap->addr + pMap->length) { + (uintptr_t)pArchive->addr + pArchive->length) { LOGW("Bad offset to local header: %d (at %d)\n", localHdrOffset, i); goto bail; } @@ -374,7 +374,7 @@ static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap) LOGW("Integer overflow adding in parseZipArchive\n"); goto bail; } - if ((size_t)pEntry->offset + pEntry->compLen > pMap->length) { + if ((size_t)pEntry->offset + pEntry->compLen > pArchive->length) { LOGW("Data ran off the end (at %d)\n", i); goto bail; } @@ -427,50 +427,30 @@ bail: * * On success, we fill out the contents of "pArchive". */ -int mzOpenZipArchive(const char* fileName, ZipArchive* pArchive) +int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive) { - MemMapping map; int err; - LOGV("Opening archive '%s' %p\n", fileName, pArchive); - - map.addr = NULL; - memset(pArchive, 0, sizeof(*pArchive)); - - pArchive->fd = open(fileName, O_RDONLY, 0); - if (pArchive->fd < 0) { - err = errno ? errno : -1; - LOGV("Unable to open '%s': %s\n", fileName, strerror(err)); - goto bail; - } - - if (sysMapFileInShmem(pArchive->fd, &map) != 0) { - err = -1; - LOGW("Map of '%s' failed\n", fileName); - goto bail; - } - - if (map.length < ENDHDR) { + if (length < ENDHDR) { err = -1; LOGV("File '%s' too small to be zip (%zd)\n", fileName, map.length); goto bail; } - if (!parseZipArchive(pArchive, &map)) { + pArchive->addr = addr; + pArchive->length = length; + + if (!parseZipArchive(pArchive)) { err = -1; LOGV("Parsing '%s' failed\n", fileName); goto bail; } err = 0; - sysCopyMap(&pArchive->map, &map); - map.addr = NULL; bail: if (err != 0) mzCloseZipArchive(pArchive); - if (map.addr != NULL) - sysReleaseShmem(&map); return err; } @@ -483,16 +463,10 @@ void mzCloseZipArchive(ZipArchive* pArchive) { LOGV("Closing archive %p\n", pArchive); - if (pArchive->fd >= 0) - close(pArchive->fd); - if (pArchive->map.addr != NULL) - sysReleaseShmem(&pArchive->map); - free(pArchive->pEntries); mzHashTableFree(pArchive->pHash); - pArchive->fd = -1; pArchive->pHash = NULL; pArchive->pEntries = NULL; } @@ -528,29 +502,7 @@ static bool processStoredEntry(const ZipArchive *pArchive, const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction, void *cookie) { - size_t bytesLeft = pEntry->compLen; - while (bytesLeft > 0) { - unsigned char buf[32 * 1024]; - ssize_t n; - size_t count; - bool ret; - - count = bytesLeft; - if (count > sizeof(buf)) { - count = sizeof(buf); - } - n = read(pArchive->fd, buf, count); - if (n < 0 || (size_t)n != count) { - LOGE("Can't read %zu bytes from zip file: %ld\n", count, n); - return false; - } - ret = processFunction(buf, n, cookie); - if (!ret) { - return false; - } - bytesLeft -= count; - } - return true; + return processFunction(pArchive->addr + pEntry->offset, pEntry->uncompLen, cookie); } static bool processDeflatedEntry(const ZipArchive *pArchive, @@ -573,8 +525,8 @@ static bool processDeflatedEntry(const ZipArchive *pArchive, zstream.zalloc = Z_NULL; zstream.zfree = Z_NULL; zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; + zstream.next_in = pArchive->addr + pEntry->offset; + zstream.avail_in = pEntry->compLen; zstream.next_out = (Bytef*) procBuf; zstream.avail_out = sizeof(procBuf); zstream.data_type = Z_UNKNOWN; @@ -598,25 +550,6 @@ static bool processDeflatedEntry(const ZipArchive *pArchive, * Loop while we have data. */ do { - /* read as much as we can */ - if (zstream.avail_in == 0) { - long getSize = (compRemaining > (long)sizeof(readBuf)) ? - (long)sizeof(readBuf) : compRemaining; - LOGVV("+++ reading %ld bytes (%ld left)\n", - getSize, compRemaining); - - int cc = read(pArchive->fd, readBuf, getSize); - if (cc != (int) getSize) { - LOGW("inflate read failed (%d vs %ld)\n", cc, getSize); - goto z_bail; - } - - compRemaining -= getSize; - - zstream.next_in = readBuf; - zstream.avail_in = getSize; - } - /* uncompress the data */ zerr = inflate(&zstream, Z_NO_FLUSH); if (zerr != Z_OK && zerr != Z_STREAM_END) { @@ -676,12 +609,6 @@ bool mzProcessZipEntryContents(const ZipArchive *pArchive, bool ret = false; off_t oldOff; - /* save current offset */ - oldOff = lseek(pArchive->fd, 0, SEEK_CUR); - - /* Seek to the beginning of the entry's compressed data. */ - lseek(pArchive->fd, pEntry->offset, SEEK_SET); - switch (pEntry->compression) { case STORED: ret = processStoredEntry(pArchive, pEntry, processFunction, cookie); @@ -695,8 +622,6 @@ bool mzProcessZipEntryContents(const ZipArchive *pArchive, break; } - /* restore file offset */ - lseek(pArchive->fd, oldOff, SEEK_SET); return ret; } diff --git a/minzip/Zip.h b/minzip/Zip.h index c94282827..05a2e60e0 100644 --- a/minzip/Zip.h +++ b/minzip/Zip.h @@ -46,11 +46,11 @@ typedef struct ZipEntry { * One Zip archive. Treat as opaque. */ typedef struct ZipArchive { - int fd; - unsigned int numEntries; - ZipEntry* pEntries; - HashTable* pHash; // maps file name to ZipEntry - MemMapping map; + unsigned int numEntries; + ZipEntry* pEntries; + HashTable* pHash; // maps file name to ZipEntry + unsigned char* addr; + size_t length; } ZipArchive; /* @@ -68,7 +68,7 @@ typedef struct { * On success, returns 0 and populates "pArchive". Returns nonzero errno * value on failure. */ -int mzOpenZipArchive(const char* fileName, ZipArchive* pArchive); +int mzOpenZipArchive(unsigned char* addr, size_t length, ZipArchive* pArchive); /* * Close archive, releasing resources associated with it. diff --git a/roots.cpp b/roots.cpp index 47cea0bec..28004a79f 100644 --- a/roots.cpp +++ b/roots.cpp @@ -238,10 +238,16 @@ int setup_install_mounts() { if (strcmp(v->mount_point, "/tmp") == 0 || strcmp(v->mount_point, "/cache") == 0) { - if (ensure_path_mounted(v->mount_point) != 0) return -1; + if (ensure_path_mounted(v->mount_point) != 0) { + LOGE("failed to mount %s\n", v->mount_point); + return -1; + } } else { - if (ensure_path_unmounted(v->mount_point) != 0) return -1; + if (ensure_path_unmounted(v->mount_point) != 0) { + LOGE("failed to unmount %s\n", v->mount_point); + return -1; + } } } return 0; diff --git a/updater/updater.c b/updater/updater.c index c7009feac..4e1cc9c38 100644 --- a/updater/updater.c +++ b/updater/updater.c @@ -22,6 +22,7 @@ #include "updater.h" #include "install.h" #include "minzip/Zip.h" +#include "minzip/SysUtil.h" // Generated by the makefile, this function defines the // RegisterDeviceExtensions() function, which calls all the @@ -65,19 +66,24 @@ int main(int argc, char** argv) { // Extract the script from the package. - char* package_data = argv[3]; + const char* package_filename = argv[3]; + MemMapping map; + if (sysMapFile(package_filename, &map) != 0) { + printf("failed to map package %s\n", argv[3]); + return 3; + } ZipArchive za; int err; - err = mzOpenZipArchive(package_data, &za); + err = mzOpenZipArchive(map.addr, map.length, &za); if (err != 0) { printf("failed to open package %s: %s\n", - package_data, strerror(err)); + argv[3], strerror(err)); return 3; } const ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME); if (script_entry == NULL) { - printf("failed to find %s in %s\n", SCRIPT_NAME, package_data); + printf("failed to find %s in %s\n", SCRIPT_NAME, package_filename); return 4; } @@ -152,6 +158,7 @@ int main(int argc, char** argv) { if (updater_info.package_zip) { mzCloseZipArchive(updater_info.package_zip); } + sysReleaseMap(&map); free(script); return 0; diff --git a/verifier.cpp b/verifier.cpp index 0930fbd15..55d58ee22 100644 --- a/verifier.cpp +++ b/verifier.cpp @@ -111,15 +111,10 @@ static bool read_pkcs7(uint8_t* pkcs7_der, size_t pkcs7_der_len, uint8_t** sig_d // Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered // or no key matches the signature). -int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys) { +int verify_file(unsigned char* addr, size_t length, + const Certificate* pKeys, unsigned int numKeys) { ui->SetProgress(0.0); - FILE* f = fopen(path, "rb"); - if (f == NULL) { - LOGE("failed to open %s (%s)\n", path, strerror(errno)); - return VERIFY_FAILURE; - } - // An archive with a whole-file signature will end in six bytes: // // (2-byte signature start) $ff $ff (2-byte comment size) @@ -131,22 +126,15 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys #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); + if (length < FOOTER_SIZE) { + LOGE("not big enough to contain footer\n"); return VERIFY_FAILURE; } - 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; - } + unsigned char* footer = addr + length - FOOTER_SIZE; if (footer[2] != 0xff || footer[3] != 0xff) { LOGE("footer is wrong\n"); - fclose(f); return VERIFY_FAILURE; } @@ -157,7 +145,6 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys if (signature_start <= FOOTER_SIZE) { LOGE("Signature start is in the footer"); - fclose(f); return VERIFY_FAILURE; } @@ -167,9 +154,8 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys // comment length. size_t eocd_size = comment_size + EOCD_HEADER_SIZE; - if (fseek(f, -eocd_size, SEEK_END) != 0) { - LOGE("failed to seek in %s (%s)\n", path, strerror(errno)); - fclose(f); + if (length < eocd_size) { + LOGE("not big enough to contain EOCD\n"); return VERIFY_FAILURE; } @@ -177,26 +163,15 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys // 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; + size_t signed_len = length - eocd_size + EOCD_HEADER_SIZE - 2; - unsigned char* eocd = (unsigned char*)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; - } + unsigned char* eocd = addr + length - eocd_size; // 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; } @@ -209,7 +184,6 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys // 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; } } @@ -229,35 +203,23 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys SHA256_CTX sha256_ctx; SHA_init(&sha1_ctx); SHA256_init(&sha256_ctx); - unsigned char* buffer = (unsigned char*)malloc(BUFFER_SIZE); - if (buffer == NULL) { - LOGE("failed to alloc memory for sha1 buffer\n"); - fclose(f); - return VERIFY_FAILURE; - } double frac = -1.0; size_t so_far = 0; - fseek(f, 0, SEEK_SET); while (so_far < signed_len) { - size_t 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; - } - if (need_sha1) SHA_update(&sha1_ctx, buffer, size); - if (need_sha256) SHA256_update(&sha256_ctx, buffer, size); + size_t size = signed_len - so_far; + if (size > BUFFER_SIZE) size = BUFFER_SIZE; + + if (need_sha1) SHA_update(&sha1_ctx, addr + so_far, size); + if (need_sha256) SHA256_update(&sha256_ctx, addr + so_far, size); so_far += size; + double f = so_far / (double)signed_len; if (f > frac + 0.02 || size == so_far) { ui->SetProgress(f); frac = f; } } - fclose(f); - free(buffer); const uint8_t* sha1 = SHA_final(&sha1_ctx); const uint8_t* sha256 = SHA256_final(&sha256_ctx); @@ -269,10 +231,8 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys if (!read_pkcs7(eocd + eocd_size - signature_start, signature_size, &sig_der, &sig_der_length)) { LOGE("Could not find signature DER block\n"); - free(eocd); return VERIFY_FAILURE; } - free(eocd); /* * Check to make sure at least one of the keys matches the signature. Since diff --git a/verifier.h b/verifier.h index 023d3bf89..15f8d98e4 100644 --- a/verifier.h +++ b/verifier.h @@ -37,10 +37,13 @@ typedef struct { ECPublicKey* ec; } Certificate; -/* Look in the file for a signature footer, and verify that it - * matches one of the given keys. Return one of the constants below. +/* addr and length define a an update package file that has been + * loaded (or mmap'ed, or whatever) into memory. Verify that the file + * is signed and the signature matches one of the given keys. Return + * one of the constants below. */ -int verify_file(const char* path, const Certificate *pKeys, unsigned int numKeys); +int verify_file(unsigned char* addr, size_t length, + const Certificate *pKeys, unsigned int numKeys); Certificate* load_keys(const char* filename, int* numKeys); diff --git a/verifier_test.cpp b/verifier_test.cpp index 88fcad4ea..10a5ddaad 100644 --- a/verifier_test.cpp +++ b/verifier_test.cpp @@ -17,12 +17,16 @@ #include #include #include +#include +#include +#include #include "common.h" #include "verifier.h" #include "ui.h" #include "mincrypt/sha.h" #include "mincrypt/sha256.h" +#include "minzip/SysUtil.h" // This is build/target/product/security/testkey.x509.pem after being // dumped out by dumpkey.jar. @@ -227,7 +231,13 @@ int main(int argc, char **argv) { ui = new FakeUI(); - int result = verify_file(argv[argn], certs, num_keys); + MemMapping map; + if (sysMapFile(argv[argn], &map) != 0) { + fprintf(stderr, "failed to mmap %s: %s\n", argv[argn], strerror(errno)); + return 4; + } + + int result = verify_file(map.addr, map.length, certs, num_keys); if (result == VERIFY_SUCCESS) { printf("VERIFIED\n"); return 0; -- cgit v1.2.3 From 76adfc5309936a07218ce53b5ab284d5746fa84c Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 13 Jan 2014 10:04:25 -0800 Subject: program to store unencrypted files in an encrypted filesystem uncrypt can read a file on an encrypted filesystem and rewrite it to the same blocks on the underlying (unencrypted) block device. This destroys the contents of the file as far as the encrypted filesystem is concerned, but allows the data to be read without the encryption key if you know which blocks of the raw device to access. uncrypt produces a "block map" file which lists the blocks that contain the file. For unencrypted filesystem, uncrypt will produce the block map without touching the data. Bug: 12188746 Change-Id: Ib7259b9e14dac8af406796b429d58378a00c7c63 --- Android.mk | 1 + uncrypt/Android.mk | 28 ++++ uncrypt/uncrypt.c | 377 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 406 insertions(+) create mode 100644 uncrypt/Android.mk create mode 100644 uncrypt/uncrypt.c diff --git a/Android.mk b/Android.mk index bd8d09b80..b1e3798c8 100644 --- a/Android.mk +++ b/Android.mk @@ -113,5 +113,6 @@ include $(LOCAL_PATH)/minui/Android.mk \ $(LOCAL_PATH)/tests/Android.mk \ $(LOCAL_PATH)/tools/Android.mk \ $(LOCAL_PATH)/edify/Android.mk \ + $(LOCAL_PATH)/uncrypt/Android.mk \ $(LOCAL_PATH)/updater/Android.mk \ $(LOCAL_PATH)/applypatch/Android.mk diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk new file mode 100644 index 000000000..756bc964c --- /dev/null +++ b/uncrypt/Android.mk @@ -0,0 +1,28 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := uncrypt.c + +LOCAL_MODULE := uncrypt + +LOCAL_STATIC_LIBRARIES := \ + libfs_mgr \ + libcutils \ + libc + +include $(BUILD_EXECUTABLE) diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c new file mode 100644 index 000000000..1f0f59dba --- /dev/null +++ b/uncrypt/uncrypt.c @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This program takes a file on an ext4 filesystem and produces a list +// of the blocks that file occupies, which enables the file contents +// to be read directly from the block device without mounting the +// filesystem. +// +// If the filesystem is using an encrypted block device, it will also +// read the file and rewrite it to the same blocks of the underlying +// (unencrypted) block device, so the file contents can be read +// without the need for the decryption key. +// +// The output of this program is a "block map" which looks like this: +// +// /dev/block/platform/msm_sdcc.1/by-name/userdata # block device +// 49652 4096 # file size in bytes, block size +// 3 # count of block ranges +// 1000 1008 # block range 0 +// 2100 2102 # ... block range 1 +// 30 33 # ... block range 2 +// +// Each block range represents a half-open interval; the line "30 33" +// reprents the blocks [30, 31, 32]. +// +// Recovery can take this block map file and retrieve the underlying +// file data to use as an update package. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define WINDOW_SIZE 5 +#define RECOVERY_COMMAND_FILE "/cache/recovery/command" +#define RECOVERY_COMMAND_FILE_TMP "/cache/recovery/command.tmp" +#define CACHE_BLOCK_MAP "/cache/recovery/block.map" + +static int write_at_offset(unsigned char* buffer, size_t size, + int wfd, off64_t offset) +{ + lseek64(wfd, offset, SEEK_SET); + size_t written = 0; + while (written < size) { + ssize_t wrote = write(wfd, buffer + written, size - written); + if (wrote < 0) { + fprintf(stderr, "error writing offset %lld: %s\n", offset, strerror(errno)); + return -1; + } + written += wrote; + } + return 0; +} + +void add_block_to_ranges(int** ranges, int* range_alloc, int* range_used, int new_block) +{ + // If the current block start is < 0, set the start to the new + // block. (This only happens for the very first block of the very + // first range.) + if ((*ranges)[*range_used*2-2] < 0) { + (*ranges)[*range_used*2-2] = new_block; + (*ranges)[*range_used*2-1] = new_block; + } + + if (new_block == (*ranges)[*range_used*2-1]) { + // If the new block comes immediately after the current range, + // all we have to do is extend the current range. + ++(*ranges)[*range_used*2-1]; + } else { + // We need to start a new range. + + // If there isn't enough room in the array, we need to expand it. + if (*range_used >= *range_alloc) { + *range_alloc *= 2; + *ranges = realloc(*ranges, *range_alloc * 2 * sizeof(int)); + } + + ++*range_used; + (*ranges)[*range_used*2-2] = new_block; + (*ranges)[*range_used*2-1] = new_block+1; + } +} + +const char* find_block_device(const char* path, int* encryptable, int* encrypted) +{ + // The fstab path is always "/fstab.${ro.hardware}". + char fstab_path[PATH_MAX+1] = "/fstab."; + if (!property_get("ro.hardware", fstab_path+strlen(fstab_path), "")) { + fprintf(stderr, "failed to get ro.hardware\n"); + return NULL; + } + + struct fstab* fstab = fs_mgr_read_fstab(fstab_path); + if (!fstab) { + fprintf(stderr, "failed to read %s\n", fstab_path); + return NULL; + } + + // Look for a volume whose mount point is the prefix of path and + // return its block device. Set encrypted if it's currently + // encrypted. + int i; + for (i = 0; i < fstab->num_entries; ++i) { + struct fstab_rec* v = &fstab->recs[i]; + if (!v->mount_point) continue; + int len = strlen(v->mount_point); + if (strncmp(path, v->mount_point, len) == 0 && + (path[len] == '/' || path[len] == 0)) { + *encrypted = 0; + *encryptable = 0; + if (fs_mgr_is_encryptable(v)) { + *encryptable = 1; + char buffer[PROPERTY_VALUE_MAX+1]; + if (property_get("ro.crypto.state", buffer, "") && + strcmp(buffer, "encrypted") == 0) { + *encrypted = 1; + } + } + return v->blk_device; + } + } + + return NULL; +} + +char* parse_recovery_command_file() +{ + char* fn = NULL; + int count = 0; + char temp[1024]; + + FILE* fo = fopen(RECOVERY_COMMAND_FILE_TMP, "w"); + + FILE* f = fopen(RECOVERY_COMMAND_FILE, "r"); + while (fgets(temp, sizeof(temp), f)) { + printf("read: %s", temp); + if (strncmp(temp, "--update_package=", strlen("--update_package=")) == 0) { + fn = strdup(temp + strlen("--update_package=")); + strcpy(temp, "--update_package=@" CACHE_BLOCK_MAP "\n"); + } + fputs(temp, fo); + } + fclose(f); + fclose(fo); + + if (fn) { + char* newline = strchr(fn, '\n'); + if (newline) *newline = 0; + } + return fn; +} + +int produce_block_map(const char* path, const char* map_file, const char* blk_dev, + int encrypted) +{ + struct stat sb; + int ret; + + FILE* mapf = fopen(map_file, "w"); + + ret = stat(path, &sb); + if (ret != 0) { + fprintf(stderr, "failed to stat %s\n", path); + return -1; + } + + printf(" block size: %ld bytes\n", sb.st_blksize); + + int blocks = ((sb.st_size-1) / sb.st_blksize) + 1; + printf(" file size: %lld bytes, %d blocks\n", sb.st_size, blocks); + + int* ranges; + int range_alloc = 1; + int range_used = 1; + ranges = malloc(range_alloc * 2 * sizeof(int)); + ranges[0] = -1; + ranges[1] = -1; + + fprintf(mapf, "%s\n%lld %lu\n", blk_dev, sb.st_size, sb.st_blksize); + + unsigned char* buffers[WINDOW_SIZE]; + int i; + if (encrypted) { + for (i = 0; i < WINDOW_SIZE; ++i) { + buffers[i] = malloc(sb.st_blksize); + } + } + int head_block = 0; + int head = 0, tail = 0; + size_t pos = 0; + + int fd = open(path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "failed to open fd for reading: %s\n", strerror(errno)); + return -1; + } + fsync(fd); + + int wfd = -1; + if (encrypted) { + wfd = open(blk_dev, O_WRONLY); + if (wfd < 0) { + fprintf(stderr, "failed to open fd for writing: %s\n", strerror(errno)); + return -1; + } + } + + while (pos < sb.st_size) { + if ((tail+1) % WINDOW_SIZE == head) { + // write out head buffer + int block = head_block; + ret = ioctl(fd, FIBMAP, &block); + if (ret != 0) { + fprintf(stderr, "failed to find block %d\n", head_block); + return -1; + } + add_block_to_ranges(&ranges, &range_alloc, &range_used, block); + if (encrypted) { + if (write_at_offset(buffers[head], sb.st_blksize, wfd, (off64_t)sb.st_blksize * block) != 0) { + return -1; + } + } + head = (head + 1) % WINDOW_SIZE; + ++head_block; + } + + // read next block to tail + if (encrypted) { + size_t so_far = 0; + while (so_far < sb.st_blksize && pos < sb.st_size) { + ssize_t this_read = read(fd, buffers[tail] + so_far, sb.st_blksize - so_far); + if (this_read < 0) { + fprintf(stderr, "failed to read: %s\n", strerror(errno)); + return -1; + } + so_far += this_read; + pos += this_read; + } + } else { + // If we're not encrypting; we don't need to actually read + // anything, just skip pos forward as if we'd read a + // block. + pos += sb.st_blksize; + } + tail = (tail+1) % WINDOW_SIZE; + } + + while (head != tail) { + // write out head buffer + int block = head_block; + ret = ioctl(fd, FIBMAP, &block); + if (ret != 0) { + fprintf(stderr, "failed to find block %d\n", head_block); + return -1; + } + add_block_to_ranges(&ranges, &range_alloc, &range_used, block); + if (encrypted) { + if (write_at_offset(buffers[head], sb.st_blksize, wfd, (off64_t)sb.st_blksize * block) != 0) { + return -1; + } + } + head = (head + 1) % WINDOW_SIZE; + ++head_block; + } + + fprintf(mapf, "%d\n", range_used); + for (i = 0; i < range_used; ++i) { + fprintf(mapf, "%d %d\n", ranges[i*2], ranges[i*2+1]); + } + + fclose(mapf); + close(fd); + if (encrypted) { + close(wfd); + } + + return 0; +} + +void reboot_to_recovery() { + property_set("sys.powerctl", "reboot,recovery"); + sleep(10); +} + +int main(int argc, char** argv) +{ + const char* input_path; + const char* map_file; + int do_reboot = 1; + + if (argc != 1 && argc != 3) { + fprintf(stderr, "usage: %s [ ]\n", argv[0]); + return 2; + } + + if (argc == 3) { + // when command-line args are given this binary is being used + // for debugging; don't reboot to recovery at the end. + input_path = argv[1]; + map_file = argv[2]; + do_reboot = 0; + } else { + input_path = parse_recovery_command_file(); + if (input_path == NULL) { + // if we're rebooting to recovery without a package (say, + // to wipe data), then we don't need to do anything before + // going to recovery. + fprintf(stderr, "no recovery command file or no update package arg"); + reboot_to_recovery(); + return 1; + } + map_file = CACHE_BLOCK_MAP; + } + + // Turn the name of the file we're supposed to convert into an + // absolute path, so we can find what filesystem it's on. + char path[PATH_MAX+1]; + if (realpath(input_path, path) == NULL) { + fprintf(stderr, "failed to convert %s to absolute path: %s\n", input_path, strerror(errno)); + return 1; + } + + int encryptable; + int encrypted; + const char* blk_dev = find_block_device(path, &encryptable, &encrypted); + if (blk_dev == NULL) { + fprintf(stderr, "failed to find block device for %s\n", path); + return 1; + } + + // If the filesystem it's on isn't encrypted, we only produce the + // block map, we don't rewrite the file contents (it would be + // pointless to do so). + printf("encryptable: %s\n", encryptable ? "yes" : "no"); + printf(" encrypted: %s\n", encrypted ? "yes" : "no"); + + if (!encryptable) { + // If the file is on a filesystem that doesn't support + // encryption (eg, /cache), then leave it alone. + // + // TODO: change this to be !encrypted -- if the file is on + // /data but /data isn't encrypted, we don't need to use the + // block map mechanism. We do for now so as to get more + // testing of it (since most dogfood devices aren't + // encrypted). + + unlink(RECOVERY_COMMAND_FILE_TMP); + } else { + if (produce_block_map(path, map_file, blk_dev, encrypted) != 0) { + return 1; + } + } + + rename(RECOVERY_COMMAND_FILE_TMP, RECOVERY_COMMAND_FILE); + reboot_to_recovery(); + return 0; +} -- cgit v1.2.3 From 65ad928602479379187d6fbe1d924f0fd60bea0e Mon Sep 17 00:00:00 2001 From: Da Zhou Date: Fri, 17 Jan 2014 01:04:07 -0800 Subject: ADB sideload command not work In kernel(3.10) USB ADB gadget driver is removed. Using Functionfs for USB adb gadget. Android recovery uses a stripped down version of adb command for sideload only. It's missing the ffs function support, so add the ffs support to allow sideload command to work b/12608946 Change-Id: I4ad024723dfc5bdb544548391f99637c390b171e Signed-off-by: Pierre Couillaud --- minadbd/adb.c | 2 +- minadbd/adb.h | 11 ++ minadbd/usb_linux_client.c | 356 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 358 insertions(+), 11 deletions(-) diff --git a/minadbd/adb.c b/minadbd/adb.c index 948f4b755..7291b4bd5 100644 --- a/minadbd/adb.c +++ b/minadbd/adb.c @@ -387,7 +387,7 @@ int adb_main() init_transport_registration(); // The minimal version of adbd only uses USB. - if (access("/dev/android_adb", F_OK) == 0) { + if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) { // listen on USB usb_init(); } diff --git a/minadbd/adb.h b/minadbd/adb.h index 98fa5972e..1f28279d3 100644 --- a/minadbd/adb.h +++ b/minadbd/adb.h @@ -410,6 +410,17 @@ extern int SHELL_EXIT_NOTIFY_FD; #define CHUNK_SIZE (64*1024) +#if !ADB_HOST +#define USB_ADB_PATH "/dev/android_adb" + +#define USB_FFS_ADB_PATH "/dev/usb-ffs/adb/" +#define USB_FFS_ADB_EP(x) USB_FFS_ADB_PATH#x + +#define USB_FFS_ADB_EP0 USB_FFS_ADB_EP(ep0) +#define USB_FFS_ADB_OUT USB_FFS_ADB_EP(ep1) +#define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2) +#endif + int sendfailmsg(int fd, const char *reason); int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s); diff --git a/minadbd/usb_linux_client.c b/minadbd/usb_linux_client.c index 635fa4bbb..c135d6396 100644 --- a/minadbd/usb_linux_client.c +++ b/minadbd/usb_linux_client.c @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include #include @@ -29,12 +31,114 @@ #define TRACE_TAG TRACE_USB #include "adb.h" +#define MAX_PACKET_SIZE_FS 64 +#define MAX_PACKET_SIZE_HS 512 + +#define cpu_to_le16(x) htole16(x) +#define cpu_to_le32(x) htole32(x) struct usb_handle { int fd; adb_cond_t notify; adb_mutex_t lock; + + int (*write)(usb_handle *h, const void *data, int len); + int (*read)(usb_handle *h, void *data, int len); + void (*kick)(usb_handle *h); + + int control; + int bulk_out; /* "out" from the host's perspective => source for adbd */ + int bulk_in; /* "in" from the host's perspective => sink for adbd */ +}; + +static const struct { + struct usb_functionfs_descs_head header; + struct { + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor_no_audio source; + struct usb_endpoint_descriptor_no_audio sink; + } __attribute__((packed)) fs_descs, hs_descs; +} __attribute__((packed)) descriptors = { + .header = { + .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC), + .length = cpu_to_le32(sizeof(descriptors)), + .fs_count = 3, + .hs_count = 3, + }, + .fs_descs = { + .intf = { + .bLength = sizeof(descriptors.fs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = ADB_CLASS, + .bInterfaceSubClass = ADB_SUBCLASS, + .bInterfaceProtocol = ADB_PROTOCOL, + .iInterface = 1, /* first string from the provided table */ + }, + .source = { + .bLength = sizeof(descriptors.fs_descs.source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_FS, + }, + .sink = { + .bLength = sizeof(descriptors.fs_descs.sink), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_FS, + }, + }, + .hs_descs = { + .intf = { + .bLength = sizeof(descriptors.hs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = ADB_CLASS, + .bInterfaceSubClass = ADB_SUBCLASS, + .bInterfaceProtocol = ADB_PROTOCOL, + .iInterface = 1, /* first string from the provided table */ + }, + .source = { + .bLength = sizeof(descriptors.hs_descs.source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_HS, + }, + .sink = { + .bLength = sizeof(descriptors.hs_descs.sink), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_HS, + }, + }, +}; + +#define STR_INTERFACE_ "ADB Interface" + +static const struct { + struct usb_functionfs_strings_head header; + struct { + __le16 code; + const char str1[sizeof(STR_INTERFACE_)]; + } __attribute__((packed)) lang0; +} __attribute__((packed)) strings = { + .header = { + .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), + .length = cpu_to_le32(sizeof(strings)), + .str_count = cpu_to_le32(1), + .lang_count = cpu_to_le32(1), + }, + .lang0 = { + cpu_to_le16(0x0409), /* en-us */ + STR_INTERFACE_, + }, }; void usb_cleanup() @@ -42,7 +146,7 @@ void usb_cleanup() // nothing to do here } -static void *usb_open_thread(void *x) +static void *usb_adb_open_thread(void *x) { struct usb_handle *usb = (struct usb_handle *)x; int fd; @@ -61,6 +165,7 @@ static void *usb_open_thread(void *x) if (fd < 0) { // to support older kernels fd = unix_open("/dev/android", O_RDWR); + fprintf(stderr, "usb_adb_open_thread: %d\n", fd ); } if (fd < 0) { adb_sleep_ms(1000); @@ -79,7 +184,7 @@ static void *usb_open_thread(void *x) return 0; } -int usb_write(usb_handle *h, const void *data, int len) +static int usb_adb_write(usb_handle *h, const void *data, int len) { int n; @@ -94,7 +199,7 @@ int usb_write(usb_handle *h, const void *data, int len) return 0; } -int usb_read(usb_handle *h, void *data, int len) +static int usb_adb_read(usb_handle *h, void *data, int len) { int n; @@ -109,23 +214,42 @@ int usb_read(usb_handle *h, void *data, int len) return 0; } -void usb_init() +static void usb_adb_kick(usb_handle *h) +{ + D("usb_kick\n"); + adb_mutex_lock(&h->lock); + adb_close(h->fd); + h->fd = -1; + + // notify usb_adb_open_thread that we are disconnected + adb_cond_signal(&h->notify); + adb_mutex_unlock(&h->lock); +} + +static void usb_adb_init() { usb_handle *h; adb_thread_t tid; int fd; h = calloc(1, sizeof(usb_handle)); + + h->write = usb_adb_write; + h->read = usb_adb_read; + h->kick = usb_adb_kick; h->fd = -1; + adb_cond_init(&h->notify, 0); adb_mutex_init(&h->lock, 0); + fprintf(stderr, "Starting to open usb_init()\n"); // Open the file /dev/android_adb_enable to trigger // the enabling of the adb USB function in the kernel. // We never touch this file again - just leave it open // indefinitely so the kernel will know when we are running // and when we are not. fd = unix_open("/dev/android_adb_enable", O_RDWR); + fprintf(stderr, "unix_open to open usb_init(): %d\n", fd); if (fd < 0) { D("failed to open /dev/android_adb_enable\n"); } else { @@ -133,25 +257,237 @@ void usb_init() } D("[ usb_init - starting thread ]\n"); - if(adb_thread_create(&tid, usb_open_thread, h)){ + if(adb_thread_create(&tid, usb_adb_open_thread, h)){ fatal_errno("cannot create usb thread"); + fprintf(stderr, "cannot create the usb thread()\n"); } } -void usb_kick(usb_handle *h) + +static void init_functionfs(struct usb_handle *h) { - D("usb_kick\n"); + ssize_t ret; + + D("OPENING %s\n", USB_FFS_ADB_EP0); + h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR); + if (h->control < 0) { + D("[ %s: cannot open control endpoint: errno=%d]\n", USB_FFS_ADB_EP0, errno); + goto err; + } + + ret = adb_write(h->control, &descriptors, sizeof(descriptors)); + if (ret < 0) { + D("[ %s: write descriptors failed: errno=%d ]\n", USB_FFS_ADB_EP0, errno); + goto err; + } + + ret = adb_write(h->control, &strings, sizeof(strings)); + if (ret < 0) { + D("[ %s: writing strings failed: errno=%d]\n", USB_FFS_ADB_EP0, errno); + goto err; + } + + h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR); + if (h->bulk_out < 0) { + D("[ %s: cannot open bulk-out ep: errno=%d ]\n", USB_FFS_ADB_OUT, errno); + goto err; + } + + h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR); + if (h->bulk_in < 0) { + D("[ %s: cannot open bulk-in ep: errno=%d ]\n", USB_FFS_ADB_IN, errno); + goto err; + } + + return; + +err: + if (h->bulk_in > 0) { + adb_close(h->bulk_in); + h->bulk_in = -1; + } + if (h->bulk_out > 0) { + adb_close(h->bulk_out); + h->bulk_out = -1; + } + if (h->control > 0) { + adb_close(h->control); + h->control = -1; + } + return; +} + +static void *usb_ffs_open_thread(void *x) +{ + struct usb_handle *usb = (struct usb_handle *)x; + + while (1) { + // wait until the USB device needs opening + adb_mutex_lock(&usb->lock); + while (usb->control != -1) + adb_cond_wait(&usb->notify, &usb->lock); + adb_mutex_unlock(&usb->lock); + + while (1) { + init_functionfs(usb); + + if (usb->control >= 0) + break; + + adb_sleep_ms(1000); + } + + D("[ usb_thread - registering device ]\n"); + register_usb_transport(usb, 0, 1); + } + + // never gets here + return 0; +} + +static int bulk_write(int bulk_in, const char *buf, size_t length) +{ + size_t count = 0; + int ret; + + do { + ret = adb_write(bulk_in, buf + count, length - count); + if (ret < 0) { + if (errno != EINTR) + return ret; + } else { + count += ret; + } + } while (count < length); + + D("[ bulk_write done fd=%d ]\n", bulk_in); + return count; +} + +static int usb_ffs_write(usb_handle *h, const void *data, int len) +{ + int n; + + D("about to write (fd=%d, len=%d)\n", h->bulk_in, len); + n = bulk_write(h->bulk_in, data, len); + if (n != len) { + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->bulk_in, n, errno, strerror(errno)); + return -1; + } + D("[ done fd=%d ]\n", h->bulk_in); + return 0; +} + +static int bulk_read(int bulk_out, char *buf, size_t length) +{ + size_t count = 0; + int ret; + + do { + ret = adb_read(bulk_out, buf + count, length - count); + if (ret < 0) { + if (errno != EINTR) { + D("[ bulk_read failed fd=%d length=%d count=%d ]\n", + bulk_out, length, count); + return ret; + } + } else { + count += ret; + } + } while (count < length); + + return count; +} + +static int usb_ffs_read(usb_handle *h, void *data, int len) +{ + int n; + + D("about to read (fd=%d, len=%d)\n", h->bulk_out, len); + n = bulk_read(h->bulk_out, data, len); + if (n != len) { + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->bulk_out, n, errno, strerror(errno)); + return -1; + } + D("[ done fd=%d ]\n", h->bulk_out); + return 0; +} + +static void usb_ffs_kick(usb_handle *h) +{ + int err; + + err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT); + if (err < 0) + D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in, errno); + + err = ioctl(h->bulk_out, FUNCTIONFS_CLEAR_HALT); + if (err < 0) + D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno); + adb_mutex_lock(&h->lock); - adb_close(h->fd); - h->fd = -1; + adb_close(h->control); + adb_close(h->bulk_out); + adb_close(h->bulk_in); + h->control = h->bulk_out = h->bulk_in = -1; - // notify usb_open_thread that we are disconnected + // notify usb_ffs_open_thread that we are disconnected adb_cond_signal(&h->notify); adb_mutex_unlock(&h->lock); } +static void usb_ffs_init() +{ + usb_handle *h; + adb_thread_t tid; + + D("[ usb_init - using FunctionFS ]\n"); + + h = calloc(1, sizeof(usb_handle)); + + h->write = usb_ffs_write; + h->read = usb_ffs_read; + h->kick = usb_ffs_kick; + + h->control = -1; + h->bulk_out = -1; + h->bulk_out = -1; + + adb_cond_init(&h->notify, 0); + adb_mutex_init(&h->lock, 0); + + D("[ usb_init - starting thread ]\n"); + if (adb_thread_create(&tid, usb_ffs_open_thread, h)){ + fatal_errno("[ cannot create usb thread ]\n"); + } +} + +void usb_init() +{ + if (access(USB_FFS_ADB_EP0, F_OK) == 0) + usb_ffs_init(); + else + usb_adb_init(); +} + +int usb_write(usb_handle *h, const void *data, int len) +{ + return h->write(h, data, len); +} + +int usb_read(usb_handle *h, void *data, int len) +{ + return h->read(h, data, len); +} int usb_close(usb_handle *h) { // nothing to do here return 0; } + +void usb_kick(usb_handle *h) +{ + h->kick(h); +} -- cgit v1.2.3 From c52c5b8e3d690201b2ae009a2d396d1899033e3d Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Sat, 18 Jan 2014 09:22:50 -0800 Subject: adbd: switch to su domain when running as root When adbd runs as root, it should transition into the su domain. This is needed to run the adbd and shell domains in enforcing on userdebug / eng devices without breaking developer workflows. Use the new device_banner command line option. Change-Id: Ib33c0dd2dd6172035230514ac84fcaed2ecf44d6 --- etc/init.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/init.rc b/etc/init.rc index 8d49f24e6..159747e2c 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -59,7 +59,7 @@ service healthd /sbin/healthd -n service recovery /sbin/recovery seclabel u:r:recovery:s0 -service adbd /sbin/adbd recovery +service adbd /sbin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery disabled socket adbd stream 660 system system seclabel u:r:adbd:s0 -- cgit v1.2.3 From 19a8e2463c31a97121c35b4666a8e6879fa7e338 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 21 Jan 2014 09:25:41 -0800 Subject: log extra info for debugging Make recovery log its PID, and when we use a block map file, log how many ranges it contains. Change-Id: I1b4299f8163af68a770b48c029ae25e6cb45d26b --- minzip/SysUtil.c | 2 ++ recovery.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c index 2cfa39ae3..c046a8cf2 100644 --- a/minzip/SysUtil.c +++ b/minzip/SysUtil.c @@ -153,6 +153,8 @@ static int sysMapBlockFile(FILE* mapf, MemMapping* pMap) pMap->addr = reserve; pMap->length = size; + LOGI("mmapped %d ranges\n", range_count); + return 0; } diff --git a/recovery.cpp b/recovery.cpp index 43cd9dafe..0a8c3b52f 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -938,7 +938,7 @@ main(int argc, char **argv) { return 0; } - printf("Starting recovery on %s", ctime(&start)); + printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start)); load_volume_table(); ensure_path_mounted(LAST_LOG_FILE); -- cgit v1.2.3 From e787fee8f28d7cf6ab15635671ec46b1ce6bc87d Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 23 Jan 2014 16:39:22 -0800 Subject: minadbd: remove dead code Change-Id: Ia1f34a17ae582575f8cd3514ed7bc015b0a5006e --- minadbd/adb.h | 4 ---- minadbd/transport.c | 21 --------------------- 2 files changed, 25 deletions(-) diff --git a/minadbd/adb.h b/minadbd/adb.h index 98fa5972e..688a6f273 100644 --- a/minadbd/adb.h +++ b/minadbd/adb.h @@ -244,15 +244,11 @@ void kick_transport( atransport* t ); #if ADB_HOST int get_available_local_transport_index(); #endif -int init_socket_transport(atransport *t, int s, int port, int local); void init_usb_transport(atransport *t, usb_handle *usb, int state); /* for MacOS X cleanup */ void close_usb_devices(); -/* cause new transports to be init'd and added to the list */ -void register_socket_transport(int s, const char *serial, int port, int local); - /* these should only be used for the "adb disconnect" command */ void unregister_transport(atransport *t); void unregister_all_tcp_transports(); diff --git a/minadbd/transport.c b/minadbd/transport.c index ff2004932..4c0c97f75 100644 --- a/minadbd/transport.c +++ b/minadbd/transport.c @@ -678,27 +678,6 @@ retry: return result; } -void register_socket_transport(int s, const char *serial, int port, int local) -{ - atransport *t = calloc(1, sizeof(atransport)); - char buff[32]; - - if (!serial) { - snprintf(buff, sizeof buff, "T-%p", t); - serial = buff; - } - D("transport: %s init'ing for socket %d, on port %d\n", serial, s, port); - if ( init_socket_transport(t, s, port, local) < 0 ) { - adb_close(s); - free(t); - return; - } - if(serial) { - t->serial = strdup(serial); - } - register_transport(t); -} - void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable) { atransport *t = calloc(1, sizeof(atransport)); -- cgit v1.2.3 From 92cdf9c37225c6f76b96c8f137896cd9e9015bbd Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 5 Feb 2014 17:30:31 -0800 Subject: recovery: fix building with pointer-to-int errors turned on Use intptr_t/uintptr_t to cast between pointer and int to allow building with -Werror=pointer-to-int-cast and Werror=int-to-pointer-cast turned on. Cast to char* instead of unsigned int for pointer arithmetic. Change-Id: Ia862306fdcca53866b330e8cf726f3d62f2248a0 --- minadbd/services.c | 4 ++-- minui/graphics.c | 2 +- minzip/Zip.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/minadbd/services.c b/minadbd/services.c index aef37f7e4..4698528ef 100644 --- a/minadbd/services.c +++ b/minadbd/services.c @@ -46,7 +46,7 @@ void *service_bootstrap_func(void *x) static void sideload_service(int s, void *cookie) { unsigned char buf[4096]; - unsigned count = (unsigned) cookie; + unsigned count = (unsigned)(uintptr_t)cookie; int fd; fprintf(stderr, "sideload_service invoked\n"); @@ -149,7 +149,7 @@ int service_to_fd(const char *name) int ret = -1; if (!strncmp(name, "sideload:", 9)) { - ret = create_service_thread(sideload_service, (void*) atoi(name + 9)); + ret = create_service_thread(sideload_service, (void*)(uintptr_t)atoi(name + 9)); #if 0 } else if(!strncmp(name, "echo:", 5)){ ret = create_service_thread(echo_service, 0); diff --git a/minui/graphics.c b/minui/graphics.c index d75716531..a2014dca3 100644 --- a/minui/graphics.c +++ b/minui/graphics.c @@ -157,7 +157,7 @@ static int get_framebuffer(GGLSurface *fb) fb->width = vi.xres; fb->height = vi.yres; fb->stride = fi.line_length/PIXEL_SIZE; - fb->data = (void*) (((unsigned) bits) + vi.yres * fi.line_length); + fb->data = (void*) (((char*) bits) + vi.yres * fi.line_length); fb->format = PIXEL_FORMAT; memset(fb->data, 0, vi.yres * fi.line_length); diff --git a/minzip/Zip.c b/minzip/Zip.c index 439e5d9cd..f4f38a9ff 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -772,7 +772,7 @@ bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry, static bool writeProcessFunction(const unsigned char *data, int dataLen, void *cookie) { - int fd = (int)cookie; + int fd = (int)(intptr_t)cookie; ssize_t soFar = 0; while (true) { @@ -802,7 +802,7 @@ bool mzExtractZipEntryToFile(const ZipArchive *pArchive, const ZipEntry *pEntry, int fd) { bool ret = mzProcessZipEntryContents(pArchive, pEntry, writeProcessFunction, - (void*)fd); + (void*)(intptr_t)fd); if (!ret) { LOGE("Can't extract entry to file.\n"); return false; -- cgit v1.2.3 From a9300301ce0bddb6f46e1e1a7499c13b615713c6 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 10 Feb 2014 12:35:19 -0800 Subject: add mzGetStoredEntry function mzGetStoredEntry gives you a pointer and address to the data of a zip entry, assuming that entry is stored rather than deflated. Change-Id: Ifb39777c98d1d50475ef7de419cf28935f5f9965 --- minzip/Zip.c | 21 +++++++++++++++++++-- minzip/Zip.h | 11 +++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/minzip/Zip.c b/minzip/Zip.c index 6785d4ea7..abc98901c 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -703,7 +703,7 @@ static bool writeProcessFunction(const unsigned char *data, int dataLen, 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", + LOGE("Error writing %zd bytes from zip file from %p: %s\n", dataLen-soFar, data+soFar, strerror(errno)); if (errno != EINTR) { return false; @@ -712,7 +712,7 @@ static bool writeProcessFunction(const unsigned char *data, int dataLen, soFar += n; if (soFar == dataLen) return true; if (soFar > dataLen) { - LOGE("write overrun? (%ld bytes instead of %d)\n", + LOGE("write overrun? (%zd bytes instead of %d)\n", soFar, dataLen); return false; } @@ -735,6 +735,23 @@ bool mzExtractZipEntryToFile(const ZipArchive *pArchive, return true; } +/* + * Obtain a pointer to the in-memory representation of a stored entry. + */ +bool mzGetStoredEntry(const ZipArchive *pArchive, + const ZipEntry *pEntry, unsigned char **addr, size_t *length) +{ + if (pEntry->compression != STORED) { + LOGE("Can't getStoredEntry for '%s'; not stored\n", + pEntry->fileName); + return false; + } + + *addr = pArchive->addr + pEntry->offset; + *length = pEntry->uncompLen; + return true; +} + typedef struct { unsigned char* buffer; long len; diff --git a/minzip/Zip.h b/minzip/Zip.h index 05a2e60e0..2054b38a4 100644 --- a/minzip/Zip.h +++ b/minzip/Zip.h @@ -182,6 +182,17 @@ bool mzExtractZipEntryToFile(const ZipArchive *pArchive, bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive, const ZipEntry *pEntry, unsigned char* buffer); +/* + * Return a pointer and length for a given entry. The returned region + * should be valid until pArchive is closed, and should be treated as + * read-only. + * + * Only makes sense for entries which are stored (ie, not compressed). + * No guarantees are made regarding alignment of the returned pointer. + */ +bool mzGetStoredEntry(const ZipArchive *pArchive, + const ZipEntry* pEntry, unsigned char **addr, size_t *length); + /* * Inflate all entries under zipDir to the directory specified by * targetDir, which must exist and be a writable directory. -- cgit v1.2.3 From 52b4036eb820042d0309b32b579c52b63ca58b4d Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 10 Feb 2014 15:30:30 -0800 Subject: add syspatch support to updater Add the syspatch() function, which can apply xdelta3+xz patches using the libsyspatch library. Change-Id: Idc1921e449020923bcaf425a1983bec0833e47ed --- updater/Android.mk | 2 + updater/install.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 115 insertions(+), 14 deletions(-) diff --git a/updater/Android.mk b/updater/Android.mk index 67e98ecd4..2e92504cc 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -35,6 +35,8 @@ LOCAL_STATIC_LIBRARIES += libcutils liblog libstdc++ libc LOCAL_STATIC_LIBRARIES += libselinux LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. +LOCAL_STATIC_LIBRARIES += libsyspatch libxz libxdelta3 + # Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function # named "Register_()". Here we emit a little C function that # gets #included by updater.c. It calls all those registration diff --git a/updater/install.c b/updater/install.c index aebd4f34b..2cf00bf13 100644 --- a/updater/install.c +++ b/updater/install.c @@ -45,11 +45,25 @@ #include "mtdutils/mounts.h" #include "mtdutils/mtdutils.h" #include "updater.h" +#include "syspatch.h" #ifdef USE_EXT4 #include "make_ext4fs.h" #endif +// Take a sha-1 digest and return it as a newly-allocated hex string. +static char* PrintSha1(uint8_t* digest) { + char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1); + int i; + const char* alphabet = "0123456789abcdef"; + for (i = 0; i < SHA_DIGEST_SIZE; ++i) { + buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf]; + buffer[i*2+1] = alphabet[digest[i] & 0xf]; + } + buffer[i*2] = '\0'; + return buffer; +} + // mount(fs_type, partition_type, location, mount_point) // // fs_type="yaffs2" partition_type="MTD" location=partition @@ -1053,8 +1067,104 @@ Value* ApplyPatchSpaceFn(const char* name, State* state, return StringValue(strdup(CacheSizeCheck(bytes) ? "" : "t")); } +// syspatch(file, size, tgt_sha1, init_sha1, patch) + +Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 5) { + return ErrorAbort(state, "%s(): expected 5 args, got %d", name, argc); + } + + char* filename; + char* filename_size_str; + char* target_sha1; + char* init_sha1; + char* patch_filename; + uint8_t target_digest[SHA_DIGEST_SIZE]; + uint8_t init_digest[SHA_DIGEST_SIZE]; + + if (ReadArgs(state, argv, 5, &filename, &filename_size_str, + &target_sha1, &init_sha1, &patch_filename) < 0) { + return NULL; + } + + if (ParseSha1(target_sha1, target_digest) != 0) { + printf("%s(): failed to parse '%s' as target SHA-1", name, target_sha1); + memset(target_digest, 0, SHA_DIGEST_SIZE); + } + if (ParseSha1(init_sha1, init_digest) != 0) { + printf("%s(): failed to parse '%s' as init SHA-1", name, init_sha1); + memset(init_digest, 0, SHA_DIGEST_SIZE); + } + + size_t len = strtoull(filename_size_str, NULL, 0); + + SHA_CTX ctx; + SHA_init(&ctx); + FILE* src = fopen(filename, "r"); + size_t pos = 0; + unsigned char buffer[4096]; + while (pos < len) { + size_t to_read = len - pos; + if (to_read > sizeof(buffer)) to_read = sizeof(buffer); + size_t read = fread(buffer, 1, to_read, src); + if (read <= 0) { + printf("%s(): short read after %zu bytes\n", name, pos); + break; + } + SHA_update(&ctx, buffer, read); + pos += read; + } + rewind(src); + uint8_t* digest = SHA_final(&ctx); + + char* hexdigest = PrintSha1(digest); + printf(" system partition sha1 = %s\n", hexdigest); + + if (memcmp(digest, target_digest, SHA_DIGEST_SIZE) == 0) { + printf("%s(): %s is already target\n", name, filename); + fclose(src); + goto done; + } + + if (memcmp(digest, init_digest, SHA_DIGEST_SIZE) != 0) { + return ErrorAbort(state, "%s(): %s in unknown state\n", name, filename); + } + + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + + const ZipEntry* entry = mzFindZipEntry(za, patch_filename); + if (entry == NULL) { + return ErrorAbort(state, "%s(): no %s in package\n", name, patch_filename); + } + + unsigned char* patch_data; + size_t patch_len; + if (!mzGetStoredEntry(za, entry, &patch_data, &patch_len)) { + return ErrorAbort(state, "%s(): failed to get %s entry\n", name, patch_filename); + } + + FILE* tgt = fopen(filename, "r+"); + + int ret = syspatch(src, patch_data, patch_len, tgt); + + fclose(src); + fclose(tgt); + + if (ret != 0) { + return ErrorAbort(state, "%s(): patching failed\n", name); + } + + done: + free(filename_size_str); + free(target_sha1); + free(init_sha1); + free(patch_filename); + return StringValue(filename); + +} + +// apply_patch(file, size, init_sha1, tgt_sha1, patch) -// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1_1, patch_1, ...) Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc < 6 || (argc % 2) == 1) { return ErrorAbort(state, "%s(): expected at least 6 args and an " @@ -1239,19 +1349,6 @@ Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) { return StringValue(strdup(buffer)); } -// Take a sha-1 digest and return it as a newly-allocated hex string. -static char* PrintSha1(uint8_t* digest) { - char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1); - int i; - const char* alphabet = "0123456789abcdef"; - for (i = 0; i < SHA_DIGEST_SIZE; ++i) { - buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf]; - buffer[i*2+1] = alphabet[digest[i] & 0xf]; - } - buffer[i*2] = '\0'; - return buffer; -} - // sha1_check(data) // to return the sha1 of the data (given in the format returned by // read_file). @@ -1469,6 +1566,8 @@ void RegisterInstallFunctions() { RegisterFunction("apply_patch_check", ApplyPatchCheckFn); RegisterFunction("apply_patch_space", ApplyPatchSpaceFn); + RegisterFunction("syspatch", SysPatchFn); + RegisterFunction("read_file", ReadFileFn); RegisterFunction("sha1_check", Sha1CheckFn); RegisterFunction("rename", RenameFn); -- cgit v1.2.3 From a1bc148c7c81f886426c253f2ea7beb0f301f6b0 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 13 Feb 2014 15:18:19 -0800 Subject: remove 'retouch' ASLR support Older versions of android supported an ASLR system where binaries were randomly twiddled at OTA install time. Remove support for this; we now use the ASLR support in the linux kernel. Change-Id: I8348eb0d6424692668dc1a00e2416fbef6c158a2 --- Android.mk | 1 - applypatch/applypatch.c | 35 ++------- applypatch/applypatch.h | 4 +- applypatch/main.c | 4 +- minelf/Android.mk | 27 ------- minelf/Retouch.c | 196 ------------------------------------------------ minelf/Retouch.h | 45 ----------- updater/install.c | 2 +- 8 files changed, 12 insertions(+), 302 deletions(-) delete mode 100644 minelf/Android.mk delete mode 100644 minelf/Retouch.c delete mode 100644 minelf/Retouch.h diff --git a/Android.mk b/Android.mk index b1e3798c8..9773d5e83 100644 --- a/Android.mk +++ b/Android.mk @@ -106,7 +106,6 @@ include $(BUILD_EXECUTABLE) include $(LOCAL_PATH)/minui/Android.mk \ - $(LOCAL_PATH)/minelf/Android.mk \ $(LOCAL_PATH)/minzip/Android.mk \ $(LOCAL_PATH)/minadbd/Android.mk \ $(LOCAL_PATH)/mtdutils/Android.mk \ diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index cb9bc2349..c9c40c98f 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "mincrypt/sha.h" #include "applypatch.h" @@ -44,14 +45,11 @@ static int GenerateTarget(FileContents* source_file, static int mtd_partitions_scanned = 0; -// Read a file into memory; optionally (retouch_flag == RETOUCH_DO_MASK) mask -// the retouched entries back to their original value (such that SHA-1 checks -// don't fail due to randomization); store the file contents and associated +// Read a file into memory; store the file contents and associated // metadata in *file. // // Return 0 on success. -int LoadFileContents(const char* filename, FileContents* file, - int retouch_flag) { +int LoadFileContents(const char* filename, FileContents* file) { file->data = NULL; // A special 'filename' beginning with "MTD:" or "EMMC:" means to @@ -87,20 +85,6 @@ int LoadFileContents(const char* filename, FileContents* file, } fclose(f); - // apply_patch[_check] functions are blind to randomization. Randomization - // is taken care of in [Undo]RetouchBinariesFn. If there is a mismatch - // within a file, this means the file is assumed "corrupt" for simplicity. - if (retouch_flag) { - int32_t desired_offset = 0; - if (retouch_mask_data(file->data, file->size, - &desired_offset, NULL) != RETOUCH_DATA_MATCHED) { - printf("error trying to mask retouch entries\n"); - free(file->data); - file->data = NULL; - return -1; - } - } - SHA_hash(file->data, file->size, file->sha1); return 0; } @@ -579,7 +563,7 @@ int applypatch_check(const char* filename, // LoadFileContents is successful. (Useful for reading // partitions, where the filename encodes the sha1s; no need to // check them twice.) - if (LoadFileContents(filename, &file, RETOUCH_DO_MASK) != 0 || + if (LoadFileContents(filename, &file) != 0 || (num_patches > 0 && FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0)) { printf("file \"%s\" doesn't have any of expected " @@ -594,7 +578,7 @@ int applypatch_check(const char* filename, // exists and matches the sha1 we're looking for, the check still // passes. - if (LoadFileContents(CACHE_TEMP_SOURCE, &file, RETOUCH_DO_MASK) != 0) { + if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) { printf("failed to load cache file\n"); return 1; } @@ -730,8 +714,7 @@ int applypatch(const char* source_filename, const Value* copy_patch_value = NULL; // We try to load the target file into the source_file object. - if (LoadFileContents(target_filename, &source_file, - RETOUCH_DO_MASK) == 0) { + if (LoadFileContents(target_filename, &source_file) == 0) { if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) { // The early-exit case: the patch was already applied, this file // has the desired hash, nothing for us to do. @@ -750,8 +733,7 @@ int applypatch(const char* source_filename, // target file, or we did but it's different from the source file. free(source_file.data); source_file.data = NULL; - LoadFileContents(source_filename, &source_file, - RETOUCH_DO_MASK); + LoadFileContents(source_filename, &source_file); } if (source_file.data != NULL) { @@ -767,8 +749,7 @@ int applypatch(const char* source_filename, source_file.data = NULL; printf("source file is bad; trying copy\n"); - if (LoadFileContents(CACHE_TEMP_SOURCE, ©_file, - RETOUCH_DO_MASK) < 0) { + if (LoadFileContents(CACHE_TEMP_SOURCE, ©_file) < 0) { // fail. printf("failed to read copy file\n"); return 1; diff --git a/applypatch/applypatch.h b/applypatch/applypatch.h index f1f13a100..ee54c24ea 100644 --- a/applypatch/applypatch.h +++ b/applypatch/applypatch.h @@ -19,7 +19,6 @@ #include #include "mincrypt/sha.h" -#include "minelf/Retouch.h" #include "edify/expr.h" typedef struct _Patch { @@ -61,8 +60,7 @@ int applypatch_check(const char* filename, int num_patches, char** const patch_sha1_str); -int LoadFileContents(const char* filename, FileContents* file, - int retouch_flag); +int LoadFileContents(const char* filename, FileContents* file); int SaveFileContents(const char* filename, const FileContents* file); void FreeFileContents(FileContents* file); int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str, diff --git a/applypatch/main.c b/applypatch/main.c index f61db5d9e..8e9fe80ef 100644 --- a/applypatch/main.c +++ b/applypatch/main.c @@ -74,7 +74,7 @@ static int ParsePatchArgs(int argc, char** argv, (*patches)[i] = NULL; } else { FileContents fc; - if (LoadFileContents(colon, &fc, RETOUCH_DONT_MASK) != 0) { + if (LoadFileContents(colon, &fc) != 0) { goto abort; } (*patches)[i] = malloc(sizeof(Value)); @@ -103,7 +103,7 @@ int PatchMode(int argc, char** argv) { Value* bonus = NULL; if (argc >= 3 && strcmp(argv[1], "-b") == 0) { FileContents fc; - if (LoadFileContents(argv[2], &fc, RETOUCH_DONT_MASK) != 0) { + if (LoadFileContents(argv[2], &fc) != 0) { printf("failed to load bonus file %s\n", argv[2]); return 1; } diff --git a/minelf/Android.mk b/minelf/Android.mk deleted file mode 100644 index 0f41ff528..000000000 --- a/minelf/Android.mk +++ /dev/null @@ -1,27 +0,0 @@ -# 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. - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - Retouch.c - -LOCAL_C_INCLUDES += bootable/recovery - -LOCAL_MODULE := libminelf - -LOCAL_CFLAGS += -Wall - -include $(BUILD_STATIC_LIBRARY) diff --git a/minelf/Retouch.c b/minelf/Retouch.c deleted file mode 100644 index d75eec1e8..000000000 --- a/minelf/Retouch.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include "Retouch.h" -#include "applypatch/applypatch.h" - -typedef struct { - int32_t mmap_addr; - char tag[4]; /* 'P', 'R', 'E', ' ' */ -} prelink_info_t __attribute__((packed)); - -#define false 0 -#define true 1 - -static int32_t offs_prev; -static uint32_t cont_prev; - -static void init_compression_state(void) { - offs_prev = 0; - cont_prev = 0; -} - -// For details on the encoding used for relocation lists, please -// refer to build/tools/retouch/retouch-prepare.c. The intent is to -// save space by removing most of the inherent redundancy. - -static void decode_bytes(uint8_t *encoded_bytes, int encoded_size, - int32_t *dst_offset, uint32_t *dst_contents) { - if (encoded_size == 2) { - *dst_offset = offs_prev + (((encoded_bytes[0]&0x60)>>5)+1)*4; - - // if the original was negative, we need to 1-pad before applying delta - int32_t tmp = (((encoded_bytes[0] & 0x0000001f) << 8) | - encoded_bytes[1]); - if (tmp & 0x1000) tmp = 0xffffe000 | tmp; - *dst_contents = cont_prev + tmp; - } else if (encoded_size == 3) { - *dst_offset = offs_prev + (((encoded_bytes[0]&0x30)>>4)+1)*4; - - // if the original was negative, we need to 1-pad before applying delta - int32_t tmp = (((encoded_bytes[0] & 0x0000000f) << 16) | - (encoded_bytes[1] << 8) | - encoded_bytes[2]); - if (tmp & 0x80000) tmp = 0xfff00000 | tmp; - *dst_contents = cont_prev + tmp; - } else { - *dst_offset = - (encoded_bytes[0]<<24) | - (encoded_bytes[1]<<16) | - (encoded_bytes[2]<<8) | - encoded_bytes[3]; - if (*dst_offset == 0x3fffffff) *dst_offset = -1; - *dst_contents = - (encoded_bytes[4]<<24) | - (encoded_bytes[5]<<16) | - (encoded_bytes[6]<<8) | - encoded_bytes[7]; - } -} - -static uint8_t *decode_in_memory(uint8_t *encoded_bytes, - int32_t *offset, uint32_t *contents) { - int input_size, charIx; - uint8_t input[8]; - - input[0] = *(encoded_bytes++); - if (input[0] & 0x80) - input_size = 2; - else if (input[0] & 0x40) - input_size = 3; - else - input_size = 8; - - // we already read one byte.. - charIx = 1; - while (charIx < input_size) { - input[charIx++] = *(encoded_bytes++); - } - - // depends on the decoder state! - decode_bytes(input, input_size, offset, contents); - - offs_prev = *offset; - cont_prev = *contents; - - return encoded_bytes; -} - -int retouch_mask_data(uint8_t *binary_object, - int32_t binary_size, - int32_t *desired_offset, - int32_t *retouch_offset) { - retouch_info_t *r_info; - prelink_info_t *p_info; - - int32_t target_offset = 0; - if (desired_offset) target_offset = *desired_offset; - - int32_t p_offs = binary_size-sizeof(prelink_info_t); // prelink_info_t - int32_t r_offs = p_offs-sizeof(retouch_info_t); // retouch_info_t - int32_t b_offs; // retouch data blob - - // If not retouched, we say it was a match. This might get invoked on - // non-retouched binaries, so that's why we need to do this. - if (retouch_offset != NULL) *retouch_offset = target_offset; - if (r_offs < 0) return (desired_offset == NULL) ? - RETOUCH_DATA_NOTAPPLICABLE : RETOUCH_DATA_MATCHED; - p_info = (prelink_info_t *)(binary_object+p_offs); - r_info = (retouch_info_t *)(binary_object+r_offs); - if (strncmp(p_info->tag, "PRE ", 4) || - strncmp(r_info->tag, "RETOUCH ", 8)) - return (desired_offset == NULL) ? - RETOUCH_DATA_NOTAPPLICABLE : RETOUCH_DATA_MATCHED; - - b_offs = r_offs-r_info->blob_size; - if (b_offs < 0) { - printf("negative binary offset: %d = %d - %d\n", - b_offs, r_offs, r_info->blob_size); - return RETOUCH_DATA_ERROR; - } - uint8_t *b_ptr = binary_object+b_offs; - - // Retouched: let's go through the work then. - int32_t offset_candidate = target_offset; - bool offset_set = false, offset_mismatch = false; - init_compression_state(); - while (b_ptr < (uint8_t *)r_info) { - int32_t retouch_entry_offset; - uint32_t *retouch_entry; - uint32_t retouch_original_value; - - b_ptr = decode_in_memory(b_ptr, - &retouch_entry_offset, - &retouch_original_value); - if (retouch_entry_offset < (-1) || - retouch_entry_offset >= b_offs) { - printf("bad retouch_entry_offset: %d", retouch_entry_offset); - return RETOUCH_DATA_ERROR; - } - - // "-1" means this is the value in prelink_info_t, which also gets - // randomized. - if (retouch_entry_offset == -1) - retouch_entry = (uint32_t *)&(p_info->mmap_addr); - else - retouch_entry = (uint32_t *)(binary_object+retouch_entry_offset); - - if (desired_offset) - *retouch_entry = retouch_original_value + target_offset; - - // Infer the randomization shift, compare to previously inferred. - int32_t offset_of_this_entry = (int32_t)(*retouch_entry- - retouch_original_value); - if (!offset_set) { - offset_candidate = offset_of_this_entry; - offset_set = true; - } else { - if (offset_candidate != offset_of_this_entry) { - offset_mismatch = true; - printf("offset is mismatched: %d, this entry is %d," - " original 0x%x @ 0x%x", - offset_candidate, offset_of_this_entry, - retouch_original_value, retouch_entry_offset); - } - } - } - if (b_ptr > (uint8_t *)r_info) { - printf("b_ptr went too far: %p, while r_info is %p", - b_ptr, r_info); - return RETOUCH_DATA_ERROR; - } - - if (offset_mismatch) return RETOUCH_DATA_MISMATCHED; - if (retouch_offset != NULL) *retouch_offset = offset_candidate; - return RETOUCH_DATA_MATCHED; -} diff --git a/minelf/Retouch.h b/minelf/Retouch.h deleted file mode 100644 index 13bacd5ad..000000000 --- a/minelf/Retouch.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 _MINELF_RETOUCH -#define _MINELF_RETOUCH - -#include -#include - -typedef struct { - char tag[8]; /* "RETOUCH ", not zero-terminated */ - uint32_t blob_size; /* in bytes, located right before this struct */ -} retouch_info_t __attribute__((packed)); - -#define RETOUCH_DONT_MASK 0 -#define RETOUCH_DO_MASK 1 - -#define RETOUCH_DATA_ERROR 0 // This is bad. Should not happen. -#define RETOUCH_DATA_MATCHED 1 // Up to an uniform random offset. -#define RETOUCH_DATA_MISMATCHED 2 // Partially randomized, or total mess. -#define RETOUCH_DATA_NOTAPPLICABLE 3 // Not retouched. Only when inferring. - -// Mask retouching in-memory. Used before apply_patch[_check]. -// Also used to determine status of retouching after a crash. -// -// If desired_offset is not NULL, then apply retouching instead, -// and return that in retouch_offset. -int retouch_mask_data(uint8_t *binary_object, - int32_t binary_size, - int32_t *desired_offset, - int32_t *retouch_offset); -#endif diff --git a/updater/install.c b/updater/install.c index 2cf00bf13..e85ba50ae 100644 --- a/updater/install.c +++ b/updater/install.c @@ -1419,7 +1419,7 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) { v->type = VAL_BLOB; FileContents fc; - if (LoadFileContents(filename, &fc, RETOUCH_DONT_MASK) != 0) { + if (LoadFileContents(filename, &fc) != 0) { free(filename); v->size = -1; v->data = NULL; -- cgit v1.2.3 From 0d32f259cddeaf46917bdc4af3514114c206dd76 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 13 Feb 2014 15:07:56 -0800 Subject: clean up some warnings when building recovery Change-Id: I1541534ee6978ddf8d548433986679ce9507d508 --- Android.mk | 2 ++ edify/Android.mk | 2 ++ edify/expr.c | 2 -- edify/expr.h | 2 ++ edify/main.c | 7 ++----- edify/parser.y | 9 +++++++++ recovery.cpp | 2 +- updater/Android.mk | 1 + updater/install.c | 6 +++--- updater/updater.c | 3 +-- 10 files changed, 23 insertions(+), 13 deletions(-) diff --git a/Android.mk b/Android.mk index 9773d5e83..c910e7db7 100644 --- a/Android.mk +++ b/Android.mk @@ -35,6 +35,7 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true RECOVERY_API_VERSION := 3 RECOVERY_FSTAB_VERSION := 2 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) +LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_STATIC_LIBRARIES := \ libext4_utils_static \ @@ -90,6 +91,7 @@ LOCAL_MODULE := verifier_test LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_TAGS := tests LOCAL_CFLAGS += -DNO_RECOVERY_MOUNT +LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_SRC_FILES := \ verifier_test.cpp \ asn1_decoder.cpp \ diff --git a/edify/Android.mk b/edify/Android.mk index fac0ba712..61ed6fa17 100644 --- a/edify/Android.mk +++ b/edify/Android.mk @@ -23,6 +23,7 @@ LOCAL_SRC_FILES := \ LOCAL_CFLAGS := $(edify_cflags) -g -O0 LOCAL_MODULE := edify LOCAL_YACCFLAGS := -v +LOCAL_CFLAGS += -Wno-unused-parameter include $(BUILD_HOST_EXECUTABLE) @@ -34,6 +35,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(edify_src_files) LOCAL_CFLAGS := $(edify_cflags) +LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_MODULE := libedify include $(BUILD_STATIC_LIBRARY) diff --git a/edify/expr.c b/edify/expr.c index a2f1f99d7..79f6282d8 100644 --- a/edify/expr.c +++ b/edify/expr.c @@ -287,13 +287,11 @@ Value* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) { long l_int = strtol(left, &end, 10); if (left[0] == '\0' || *end != '\0') { - printf("[%s] is not an int\n", left); goto done; } long r_int = strtol(right, &end, 10); if (right[0] == '\0' || *end != '\0') { - printf("[%s] is not an int\n", right); goto done; } diff --git a/edify/expr.h b/edify/expr.h index 0d8ed8f57..a9ed2f9c5 100644 --- a/edify/expr.h +++ b/edify/expr.h @@ -164,6 +164,8 @@ Value* StringValue(char* str); // Free a Value object. void FreeValue(Value* v); +int parse_string(const char* str, Expr** root, int* error_count); + #ifdef __cplusplus } // extern "C" #endif diff --git a/edify/main.c b/edify/main.c index 9e6bab7ca..b3fad53b8 100644 --- a/edify/main.c +++ b/edify/main.c @@ -30,9 +30,7 @@ int expect(const char* expr_str, const char* expected, int* errors) { printf("."); - yy_scan_string(expr_str); - int error_count = 0; - error = yyparse(&e, &error_count); + int error_count = parse_string(expr_str, &e, &error_count); if (error > 0 || error_count > 0) { printf("error parsing \"%s\" (%d errors)\n", expr_str, error_count); @@ -193,8 +191,7 @@ int main(int argc, char** argv) { Expr* root; int error_count = 0; - yy_scan_bytes(buffer, size); - int error = yyparse(&root, &error_count); + int error = parse_string(buffer, &root, &error_count); printf("parse returned %d; %d errors encountered\n", error, error_count); if (error == 0 || error_count > 0) { diff --git a/edify/parser.y b/edify/parser.y index 3f9ade144..f8fb2d12f 100644 --- a/edify/parser.y +++ b/edify/parser.y @@ -29,6 +29,10 @@ extern int gColumn; void yyerror(Expr** root, int* error_count, const char* s); int yyparse(Expr** root, int* error_count); +struct yy_buffer_state; +void yy_switch_to_buffer(struct yy_buffer_state* new_buffer); +struct yy_buffer_state* yy_scan_string(const char* yystr); + %} %locations @@ -128,3 +132,8 @@ void yyerror(Expr** root, int* error_count, const char* s) { printf("line %d col %d: %s\n", gLine, gColumn, s); ++*error_count; } + +int parse_string(const char* str, Expr** root, int* error_count) { + yy_switch_to_buffer(yy_scan_string(str)); + return yyparse(root, error_count); +} diff --git a/recovery.cpp b/recovery.cpp index 0a8c3b52f..db35f1e97 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -980,7 +980,7 @@ main(int argc, char **argv) { load_locale_from_cache(); } printf("locale is [%s]\n", locale); - printf("stage is [%s]\n", stage, stage); + printf("stage is [%s]\n", stage); Device* device = make_device(); ui = device->GetUI(); diff --git a/updater/Android.mk b/updater/Android.mk index 2e92504cc..c21ff8ecd 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -20,6 +20,7 @@ LOCAL_SRC_FILES := $(updater_src_files) ifeq ($(TARGET_USERIMAGES_USE_EXT4), true) LOCAL_CFLAGS += -DUSE_EXT4 +LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_C_INCLUDES += system/extras/ext4_utils LOCAL_STATIC_LIBRARIES += \ libext4_utils_static \ diff --git a/updater/install.c b/updater/install.c index e85ba50ae..ccafad9c2 100644 --- a/updater/install.c +++ b/updater/install.c @@ -52,7 +52,7 @@ #endif // Take a sha-1 digest and return it as a newly-allocated hex string. -static char* PrintSha1(uint8_t* digest) { +static char* PrintSha1(const uint8_t* digest) { char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1); int i; const char* alphabet = "0123456789abcdef"; @@ -1115,9 +1115,9 @@ Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) { pos += read; } rewind(src); - uint8_t* digest = SHA_final(&ctx); + const uint8_t* digest = SHA_final(&ctx); - char* hexdigest = PrintSha1(digest); + const char* hexdigest = PrintSha1(digest); printf(" system partition sha1 = %s\n", hexdigest); if (memcmp(digest, target_digest, SHA_DIGEST_SIZE) == 0) { diff --git a/updater/updater.c b/updater/updater.c index 4e1cc9c38..b7af3e500 100644 --- a/updater/updater.c +++ b/updater/updater.c @@ -105,8 +105,7 @@ int main(int argc, char** argv) { Expr* root; int error_count = 0; - yy_scan_string(script); - int error = yyparse(&root, &error_count); + int error = parse_string(script, &root, &error_count); if (error != 0 || error_count > 0) { printf("%d parse errors\n", error_count); return 6; -- cgit v1.2.3 From 3eb681d1de4eb0a4807e851c323568ed3f360381 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 13 Feb 2014 15:49:35 -0800 Subject: remove remaining libminelf references Change-Id: Id38b08607829bccc031693cc03e60e849903b6f8 --- applypatch/Android.mk | 4 ++-- updater/Android.mk | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/applypatch/Android.mk b/applypatch/Android.mk index ef57f243c..4984093dd 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -28,7 +28,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := main.c LOCAL_MODULE := applypatch LOCAL_C_INCLUDES += bootable/recovery -LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz libminelf +LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz LOCAL_SHARED_LIBRARIES += libz libcutils libstdc++ libc include $(BUILD_EXECUTABLE) @@ -40,7 +40,7 @@ LOCAL_MODULE := applypatch_static LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_TAGS := eng LOCAL_C_INCLUDES += bootable/recovery -LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz libminelf +LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc include $(BUILD_EXECUTABLE) diff --git a/updater/Android.mk b/updater/Android.mk index 2e92504cc..ebcfef15b 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -30,7 +30,6 @@ endif 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 += libminelf LOCAL_STATIC_LIBRARIES += libcutils liblog libstdc++ libc LOCAL_STATIC_LIBRARIES += libselinux LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. -- cgit v1.2.3 From e7b288824589b2828d83e1d47f6f12b0bd0fb353 Mon Sep 17 00:00:00 2001 From: Maxim Siniavine Date: Thu, 13 Feb 2014 15:48:53 -0800 Subject: Fix a crash when going into recovery mode. When going into recovery mode withoug recovery command file present, uncrypt crashes and the device gets stuck and eventually shuts down. Check that the command file is present before trying to read from it. Change-Id: If0192d597032be0067738e437188d92993ce56f7 --- uncrypt/uncrypt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c index 1f0f59dba..7c2d99477 100644 --- a/uncrypt/uncrypt.c +++ b/uncrypt/uncrypt.c @@ -149,9 +149,14 @@ char* parse_recovery_command_file() int count = 0; char temp[1024]; - FILE* fo = fopen(RECOVERY_COMMAND_FILE_TMP, "w"); + FILE* f = fopen(RECOVERY_COMMAND_FILE, "r"); + if (f == NULL) { + return NULL; + } + FILE* fo = fopen(RECOVERY_COMMAND_FILE_TMP, "w"); + while (fgets(temp, sizeof(temp), f)) { printf("read: %s", temp); if (strncmp(temp, "--update_package=", strlen("--update_package=")) == 0) { -- cgit v1.2.3 From 1b9641127c539c73cf97d7a69fa917838043db92 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Wed, 19 Feb 2014 17:41:02 -0800 Subject: recovery: Fix adb with linux 3.10 In kernel 3.10, f_adb has been removed and adbd can use functionfs instead. Mount functionfs on boot for adbd. On older kernels, mount will fail silently and adbd will revert to f_adb. Change-Id: I5db57aaf35b35859ea88c7d0e0661d8c553e5811 --- etc/init.rc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/etc/init.rc b/etc/init.rc index 159747e2c..6e0595b44 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -28,9 +28,15 @@ on init chown root shell /tmp chmod 0775 /tmp +on fs + mkdir /dev/usb-ffs 0770 shell shell + mkdir /dev/usb-ffs/adb 0770 shell shell + mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000 + write /sys/class/android_usb/android0/enable 0 write /sys/class/android_usb/android0/idVendor 18D1 write /sys/class/android_usb/android0/idProduct D001 + write /sys/class/android_usb/android0/f_ffs/aliases adb write /sys/class/android_usb/android0/functions adb write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer} write /sys/class/android_usb/android0/iProduct ${ro.product.model} -- cgit v1.2.3 From 0ac1cbaa76f1ab88ed575b197b06d042e015c10e Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 20 Feb 2014 09:46:19 -0800 Subject: add flag for GPL license updater now depends on the GPL'd libraries libsyspatch and libxdelta3, so be careful when taking code from this directory. Change-Id: Ib6f8c50ce7052912b9d81ff96d095f778bf9a3d0 --- updater/MODULE_LICENSE_GPL | 0 updater/NOTICE | 339 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 339 insertions(+) create mode 100644 updater/MODULE_LICENSE_GPL create mode 100644 updater/NOTICE diff --git a/updater/MODULE_LICENSE_GPL b/updater/MODULE_LICENSE_GPL new file mode 100644 index 000000000..e69de29bb diff --git a/updater/NOTICE b/updater/NOTICE new file mode 100644 index 000000000..e77696ae8 --- /dev/null +++ b/updater/NOTICE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. -- cgit v1.2.3 From c9d6e4ff514b91b0ef08facb4dd480f166beae84 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 24 Feb 2014 16:02:50 -0800 Subject: support don't-care maps when writing the system image Make package_extract_file() take an optional third argument which is the pathname (in the package zip) of a map of don't-care regions to skip over when writing the file. Modify syspatch() to take source and target don't-care maps and use them when patching the system partition. Add the wipe_block_device() function to do a discard of all data on the partition. Change-Id: I8c856054edfb6aab2f3e5177f16d9d78add20be4 --- updater/install.c | 234 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 172 insertions(+), 62 deletions(-) diff --git a/updater/install.c b/updater/install.c index ccafad9c2..faa1bb2f5 100644 --- a/updater/install.c +++ b/updater/install.c @@ -46,9 +46,11 @@ #include "mtdutils/mtdutils.h" #include "updater.h" #include "syspatch.h" +#include "install.h" #ifdef USE_EXT4 #include "make_ext4fs.h" +#include "wipe.h" #endif // Take a sha-1 digest and return it as a newly-allocated hex string. @@ -428,6 +430,54 @@ Value* PackageExtractDirFn(const char* name, State* state, } +DontCareMap* ReadDontCareMapFromZip(ZipArchive* za, const char* path) { + const char* name = "ReadDontCareMapFromZip"; + + const ZipEntry* entry = mzFindZipEntry(za, path); + if (entry == NULL) { + printf("%s: no %s in package\n", name, path); + return NULL; + } + + size_t map_size = mzGetZipEntryUncompLen(entry); + char* map_data = malloc(map_size); + if (map_data == NULL) { + printf("%s: failed to allocate %zu bytes for %s\n", + name, map_size, path); + return NULL; + } + + if (!mzExtractZipEntryToBuffer(za, entry, (unsigned char*) map_data)) { + printf("%s: failed to read %s\n", name, path); + return NULL; + } + + char* p = map_data; + DontCareMap* map = (DontCareMap*) malloc(sizeof(DontCareMap)); + + map->block_size = strtoul(p, &p, 0); + if (map->block_size != 4096) { + printf("%s: unexpected block size %zu\n", name, map->block_size); + return NULL; + } + + map->region_count = strtoul(p, &p, 0); + map->regions = (int*) malloc(map->region_count * sizeof(int)); + + int i; + for (i = 0; i < map->region_count; ++i) { + map->regions[i] = strtoul(p, &p, 0); + } + + return map; +} + +bool MapWriter(const unsigned char* data, int dataLen, void* cookie) { + return write_with_map(data, dataLen, (MapState*) cookie) == dataLen; +} + +// package_extract_file(package_path, destination_path, map_path) +// or // package_extract_file(package_path, destination_path) // or // package_extract_file(package_path) @@ -435,19 +485,30 @@ Value* PackageExtractDirFn(const char* name, State* state, // function (the char* returned is actually a FileContents*). Value* PackageExtractFileFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 1 && argc != 2) { - return ErrorAbort(state, "%s() expects 1 or 2 args, got %d", + if (argc < 1 || argc > 3) { + return ErrorAbort(state, "%s() expects 1 or 2 or 3 args, got %d", name, argc); } bool success = false; - if (argc == 2) { - // The two-argument version extracts to a file. + if (argc >= 2) { + // The two-argument version extracts to a file; the three-arg + // version extracts to a file, skipping over regions in a + // don't care map. + + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; char* zip_path; char* dest_path; - if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; + char* map_path = NULL; + DontCareMap* map = NULL; + if (argc == 2) { + if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; + } else { + if (ReadArgs(state, argv, 3, &zip_path, &dest_path, &map_path) < 0) return NULL; + map = ReadDontCareMapFromZip(za, map_path); + if (map == NULL) goto done2; + } - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; const ZipEntry* entry = mzFindZipEntry(za, zip_path); if (entry == NULL) { printf("%s: no %s in package\n", name, zip_path); @@ -460,12 +521,26 @@ Value* PackageExtractFileFn(const char* name, State* state, name, dest_path, strerror(errno)); goto done2; } - success = mzExtractZipEntryToFile(za, entry, fileno(f)); + if (map) { + MapState state; + state.map = map; + state.cr = 0; + state.so_far = 0; + state.f = f; + success = mzProcessZipEntryContents(za, entry, MapWriter, &state); + } else { + success = mzExtractZipEntryToFile(za, entry, fileno(f)); + } fclose(f); done2: free(zip_path); free(dest_path); + free(map_path); + if (map) { + free(map->regions); + free(map); + } return StringValue(strdup(success ? "t" : "")); } else { // The one-argument version returns the contents of the file @@ -1067,23 +1142,48 @@ Value* ApplyPatchSpaceFn(const char* name, State* state, return StringValue(strdup(CacheSizeCheck(bytes) ? "" : "t")); } -// syspatch(file, size, tgt_sha1, init_sha1, patch) +bool CheckMappedFileSha1(FILE* f, DontCareMap* map, uint8_t* intended_digest) { + MapState state; + + state.f = f; + state.so_far = 0; + state.cr = 0; + state.map = map; + + SHA_CTX ctx; + SHA_init(&ctx); + + unsigned char buffer[32173]; + size_t bytes_read; + + while ((bytes_read = read_with_map(buffer, sizeof(buffer), &state)) > 0) { + SHA_update(&ctx, buffer, bytes_read); + } + const uint8_t* digest = SHA_final(&ctx); + + return memcmp(digest, intended_digest, SHA_DIGEST_SIZE) == 0; +} + + +// syspatch(file, tgt_mapfile, tgt_sha1, init_mapfile, init_sha1, patch) Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 5) { - return ErrorAbort(state, "%s(): expected 5 args, got %d", name, argc); + if (argc != 6) { + return ErrorAbort(state, "%s(): expected 6 args, got %d", name, argc); } char* filename; - char* filename_size_str; + char* target_mapfilename; char* target_sha1; + char* init_mapfilename; char* init_sha1; char* patch_filename; uint8_t target_digest[SHA_DIGEST_SIZE]; uint8_t init_digest[SHA_DIGEST_SIZE]; - if (ReadArgs(state, argv, 5, &filename, &filename_size_str, - &target_sha1, &init_sha1, &patch_filename) < 0) { + if (ReadArgs(state, argv, 6, &filename, + &target_mapfilename, &target_sha1, + &init_mapfilename, &init_sha1, &patch_filename) < 0) { return NULL; } @@ -1096,68 +1196,56 @@ Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) { memset(init_digest, 0, SHA_DIGEST_SIZE); } - size_t len = strtoull(filename_size_str, NULL, 0); - - SHA_CTX ctx; - SHA_init(&ctx); + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; FILE* src = fopen(filename, "r"); - size_t pos = 0; - unsigned char buffer[4096]; - while (pos < len) { - size_t to_read = len - pos; - if (to_read > sizeof(buffer)) to_read = sizeof(buffer); - size_t read = fread(buffer, 1, to_read, src); - if (read <= 0) { - printf("%s(): short read after %zu bytes\n", name, pos); - break; - } - SHA_update(&ctx, buffer, read); - pos += read; - } - rewind(src); - const uint8_t* digest = SHA_final(&ctx); - - const char* hexdigest = PrintSha1(digest); - printf(" system partition sha1 = %s\n", hexdigest); - if (memcmp(digest, target_digest, SHA_DIGEST_SIZE) == 0) { - printf("%s(): %s is already target\n", name, filename); - fclose(src); - goto done; - } + DontCareMap* init_map = ReadDontCareMapFromZip(za, init_mapfilename); + if (init_map == NULL) return ErrorAbort(state, "%s(): failed to read init map\n", name); + DontCareMap* target_map = ReadDontCareMapFromZip(za, target_mapfilename); + if (target_map == NULL) return ErrorAbort(state, "%s(): failed to read target map\n", name); - if (memcmp(digest, init_digest, SHA_DIGEST_SIZE) != 0) { - return ErrorAbort(state, "%s(): %s in unknown state\n", name, filename); - } + if (CheckMappedFileSha1(src, init_map, init_digest)) { + // If the partition contents match the init_digest, then we need to apply the patch. - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + rewind(src); - const ZipEntry* entry = mzFindZipEntry(za, patch_filename); - if (entry == NULL) { - return ErrorAbort(state, "%s(): no %s in package\n", name, patch_filename); - } + const ZipEntry* entry = mzFindZipEntry(za, patch_filename); + if (entry == NULL) { + return ErrorAbort(state, "%s(): no %s in package\n", name, patch_filename); + } - unsigned char* patch_data; - size_t patch_len; - if (!mzGetStoredEntry(za, entry, &patch_data, &patch_len)) { - return ErrorAbort(state, "%s(): failed to get %s entry\n", name, patch_filename); - } + unsigned char* patch_data; + size_t patch_len; + if (!mzGetStoredEntry(za, entry, &patch_data, &patch_len)) { + return ErrorAbort(state, "%s(): failed to get %s entry\n", name, patch_filename); + } - FILE* tgt = fopen(filename, "r+"); + FILE* tgt = fopen(filename, "r+"); - int ret = syspatch(src, patch_data, patch_len, tgt); + int ret = syspatch(src, init_map, patch_data, patch_len, tgt, target_map); - fclose(src); - fclose(tgt); + fclose(src); + fclose(tgt); - if (ret != 0) { - return ErrorAbort(state, "%s(): patching failed\n", name); + if (ret != 0) { + return ErrorAbort(state, "%s(): patching failed\n", name); + } + } else { + rewind(src); + if (CheckMappedFileSha1(src, target_map, target_digest)) { + // If the partition contents match the target already, we + // don't need to do anything. + printf("%s: output is already target\n", name); + } else { + return ErrorAbort(state, "%s(): %s in unknown state\n", name, filename); + } } - done: - free(filename_size_str); + done: free(target_sha1); + free(target_mapfilename); free(init_sha1); + free(init_mapfilename); free(patch_filename); return StringValue(filename); @@ -1516,7 +1604,7 @@ Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) { // Return the value most recently saved with SetStageFn. The argument // is the block device for the misc partition. Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 2) { + if (argc != 1) { return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); } @@ -1533,6 +1621,27 @@ Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) { return StringValue(strdup(buffer)); } +Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + + char* filename; + char* len_str; + if (ReadArgs(state, argv, 2, &filename, &len_str) < 0) return NULL; + + size_t len = strtoull(len_str, NULL, 0); + int fd = open(filename, O_WRONLY, 0644); + int success = wipe_block_device(fd, len); + + free(filename); + free(len_str); + + close(fd); + + return StringValue(strdup(success ? "t" : "")); +} + void RegisterInstallFunctions() { RegisterFunction("mount", MountFn); RegisterFunction("is_mounted", IsMountedFn); @@ -1566,6 +1675,7 @@ void RegisterInstallFunctions() { RegisterFunction("apply_patch_check", ApplyPatchCheckFn); RegisterFunction("apply_patch_space", ApplyPatchSpaceFn); + RegisterFunction("wipe_block_device", WipeBlockDeviceFn); RegisterFunction("syspatch", SysPatchFn); RegisterFunction("read_file", ReadFileFn); -- cgit v1.2.3 From a8c56072ecf826d5a1c49ea8d180092e7d6496e9 Mon Sep 17 00:00:00 2001 From: Paul Lawrence Date: Mon, 3 Mar 2014 11:22:49 -0800 Subject: Fix build break caused by vold change cryptfs.h now includes sha header from libcrypto folder Change-Id: Icd02c88971aedf96040c3bd9ca759e531546023b --- Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/Android.mk b/Android.mk index c910e7db7..3b899b5fc 100644 --- a/Android.mk +++ b/Android.mk @@ -75,6 +75,7 @@ else endif LOCAL_C_INCLUDES += system/extras/ext4_utils +LOCAL_C_INCLUDES += external/openssl/include include $(BUILD_EXECUTABLE) -- cgit v1.2.3 From eac881c952fc6be0beeb5f719e3a70e651f3610e Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 7 Mar 2014 09:21:25 -0800 Subject: change how recovery animation is implemented Instead of one 'base' installing image and a number of overlay images that are drawn on top of it, we represent the installing animation with one PNG that contains all the animation frames, interlaced by row. The PNG is expected to have a text chunk with the keyword 'Frames' and a value that's the number of frames (as an ascii string). This representation provides better compression, removes the need to subclass ScreenRecoveryUI just to change the position of the overlay or number of frames, and doesn't require gr_blit() to support an alpha channel. We also remove the 'indeterminate' progress bar used when wiping data and/or cache. The main animation serves the same purpose (showing that the device is still alive); the spinning progress bar has been redundant for a while. This changes the default recovery animation to include the antenna-wiggling and gear-turning that's used in the Nexus 5 recovery animation. Change-Id: I51930a76035ac09969a25472f4e572b289418729 --- minui/minui.h | 1 + minui/resources.c | 182 ++++++++++++++++++++++++++++++- res/images/icon_installing.png | Bin 25261 -> 118562 bytes res/images/icon_installing_overlay01.png | Bin 10095 -> 0 bytes res/images/icon_installing_overlay02.png | Bin 9990 -> 0 bytes res/images/icon_installing_overlay03.png | Bin 9782 -> 0 bytes res/images/icon_installing_overlay04.png | Bin 9817 -> 0 bytes res/images/icon_installing_overlay05.png | Bin 9863 -> 0 bytes res/images/icon_installing_overlay06.png | Bin 9944 -> 0 bytes res/images/icon_installing_overlay07.png | Bin 10062 -> 0 bytes res/images/indeterminate01.png | Bin 673 -> 0 bytes res/images/indeterminate02.png | Bin 687 -> 0 bytes res/images/indeterminate03.png | Bin 661 -> 0 bytes res/images/indeterminate04.png | Bin 665 -> 0 bytes res/images/indeterminate05.png | Bin 683 -> 0 bytes res/images/indeterminate06.png | Bin 676 -> 0 bytes screen_ui.cpp | 109 +++--------------- screen_ui.h | 9 +- 18 files changed, 202 insertions(+), 99 deletions(-) delete mode 100644 res/images/icon_installing_overlay01.png delete mode 100644 res/images/icon_installing_overlay02.png delete mode 100644 res/images/icon_installing_overlay03.png delete mode 100644 res/images/icon_installing_overlay04.png delete mode 100644 res/images/icon_installing_overlay05.png delete mode 100644 res/images/icon_installing_overlay06.png delete mode 100644 res/images/icon_installing_overlay07.png delete mode 100644 res/images/indeterminate01.png delete mode 100644 res/images/indeterminate02.png delete mode 100644 res/images/indeterminate03.png delete mode 100644 res/images/indeterminate04.png delete mode 100644 res/images/indeterminate05.png delete mode 100644 res/images/indeterminate06.png diff --git a/minui/minui.h b/minui/minui.h index 573dd71f4..3250955c7 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -73,6 +73,7 @@ int ev_get_epollfd(void); // Returns 0 if no error, else negative. int res_create_surface(const char* name, gr_surface* pSurface); +int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurface); int res_create_localized_surface(const char* name, gr_surface* pSurface); void res_free_surface(gr_surface surface); diff --git a/minui/resources.c b/minui/resources.c index b20c00abd..91b01eb14 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -189,6 +189,182 @@ exit: return result; } +int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurface) { + char resPath[256]; + int result = 0; + unsigned char header[8]; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + int i; + GGLSurface** surface = NULL; + + *pSurface = NULL; + *frames = -1; + + snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name); + resPath[sizeof(resPath)-1] = '\0'; + FILE* fp = fopen(resPath, "rb"); + if (fp == NULL) { + result = -1; + goto exit; + } + + size_t bytesRead = fread(header, 1, sizeof(header), fp); + if (bytesRead != sizeof(header)) { + result = -2; + goto exit; + } + + if (png_sig_cmp(header, 0, sizeof(header))) { + result = -3; + goto exit; + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + result = -4; + goto exit; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + result = -5; + goto exit; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + result = -6; + goto exit; + } + + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, sizeof(header)); + png_read_info(png_ptr, info_ptr); + + int color_type, bit_depth; + png_uint_32 width, height; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, + &color_type, NULL, NULL, NULL); + + int channels = png_get_channels(png_ptr, info_ptr); + + if (!(bit_depth == 8 && + ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) || + (channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) || + (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE || + color_type == PNG_COLOR_TYPE_GRAY))))) { + return -7; + goto exit; + } + + *frames = 1; + png_textp text; + int num_text; + if (png_get_text(png_ptr, info_ptr, &text, &num_text)) { + for (i = 0; i < num_text; ++i) { + if (text[i].key && strcmp(text[i].key, "Frames") == 0 && text[i].text) { + *frames = atoi(text[i].text); + break; + } + } + printf(" found frames = %d\n", *frames); + } + + if (height % *frames != 0) { + printf("bad height (%d) for frame count (%d)\n", height, *frames); + result = -9; + goto exit; + } + + size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width; + size_t pixelSize = stride * height / *frames; + + surface = malloc(*frames * sizeof(GGLSurface*)); + if (surface == NULL) { + result = -8; + goto exit; + } + for (i = 0; i < *frames; ++i) { + surface[i] = malloc(sizeof(GGLSurface) + pixelSize); + surface[i]->version = sizeof(GGLSurface); + surface[i]->width = width; + surface[i]->height = height / *frames; + surface[i]->stride = width; /* Yes, pixels, not bytes */ + surface[i]->data = (unsigned char*) (surface[i] + 1); + + if (channels == 3) { + surface[i]->format = GGL_PIXEL_FORMAT_RGBX_8888; + } else if (color_type == PNG_COLOR_TYPE_PALETTE) { + surface[i]->format = GGL_PIXEL_FORMAT_RGBA_8888; + } else if (channels == 1) { + surface[i]->format = GGL_PIXEL_FORMAT_L_8; + } else { + surface[i]->format = GGL_PIXEL_FORMAT_RGBA_8888; + } + } + + int alpha = (channels == 4); + if (color_type == PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(png_ptr); + } + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(png_ptr); + alpha = 1; + } + if (color_type == PNG_COLOR_TYPE_GRAY) { + alpha = 1; + } + + png_uint_32 y; + if (channels == 3 || (channels == 1 && !alpha)) { + for (y = 0; y < height; ++y) { + int fy = y / *frames; + int fr = y % *frames; + unsigned char* pRow = surface[fr]->data + fy * 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 r = pRow[sx]; + unsigned char g = pRow[sx + 1]; + 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 + 3] = a; + } + } + } else { + for (y = 0; y < height; ++y) { + int fy = y / *frames; + int fr = y % *frames; + unsigned char* pRow = surface[fr]->data + fy * stride; + png_read_row(png_ptr, pRow, NULL); + } + } + + *pSurface = (gr_surface*) surface; + +exit: + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + if (fp != NULL) { + fclose(fp); + } + if (result < 0) { + if (surface) { + for (i = 0; i < *frames; ++i) { + if (surface[i]) free(surface[i]); + } + free(surface); + } + } + return result; +} + static int matches_locale(const char* loc) { if (locale == NULL) return 0; @@ -257,7 +433,7 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) { png_read_info(png_ptr, info_ptr); int color_type, bit_depth; - size_t width, height; + png_uint_32 width, height; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); int channels = png_get_channels(png_ptr, info_ptr); @@ -269,13 +445,13 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) { } unsigned char* row = malloc(width); - int y; + png_uint_32 y; for (y = 0; y < height; ++y) { png_read_row(png_ptr, row, NULL); int w = (row[1] << 8) | row[0]; int h = (row[3] << 8) | row[2]; int len = row[4]; - char* loc = row+5; + char* loc = (char*)row+5; if (y+1+h >= height || matches_locale(loc)) { printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y); diff --git a/res/images/icon_installing.png b/res/images/icon_installing.png index 571eb8b0f..c2c020162 100644 Binary files a/res/images/icon_installing.png and b/res/images/icon_installing.png differ diff --git a/res/images/icon_installing_overlay01.png b/res/images/icon_installing_overlay01.png deleted file mode 100644 index e762d6cbe..000000000 Binary files a/res/images/icon_installing_overlay01.png and /dev/null differ diff --git a/res/images/icon_installing_overlay02.png b/res/images/icon_installing_overlay02.png deleted file mode 100644 index f7a853017..000000000 Binary files a/res/images/icon_installing_overlay02.png and /dev/null differ diff --git a/res/images/icon_installing_overlay03.png b/res/images/icon_installing_overlay03.png deleted file mode 100644 index 1a1d738e4..000000000 Binary files a/res/images/icon_installing_overlay03.png and /dev/null differ diff --git a/res/images/icon_installing_overlay04.png b/res/images/icon_installing_overlay04.png deleted file mode 100644 index a74903d33..000000000 Binary files a/res/images/icon_installing_overlay04.png and /dev/null differ diff --git a/res/images/icon_installing_overlay05.png b/res/images/icon_installing_overlay05.png deleted file mode 100644 index d17bdc006..000000000 Binary files a/res/images/icon_installing_overlay05.png and /dev/null differ diff --git a/res/images/icon_installing_overlay06.png b/res/images/icon_installing_overlay06.png deleted file mode 100644 index 1200b75cb..000000000 Binary files a/res/images/icon_installing_overlay06.png and /dev/null differ diff --git a/res/images/icon_installing_overlay07.png b/res/images/icon_installing_overlay07.png deleted file mode 100644 index 3838a85ad..000000000 Binary files a/res/images/icon_installing_overlay07.png and /dev/null differ diff --git a/res/images/indeterminate01.png b/res/images/indeterminate01.png deleted file mode 100644 index 933528d6d..000000000 Binary files a/res/images/indeterminate01.png and /dev/null differ diff --git a/res/images/indeterminate02.png b/res/images/indeterminate02.png deleted file mode 100644 index d760e2bdd..000000000 Binary files a/res/images/indeterminate02.png and /dev/null differ diff --git a/res/images/indeterminate03.png b/res/images/indeterminate03.png deleted file mode 100644 index 0e97399d1..000000000 Binary files a/res/images/indeterminate03.png and /dev/null differ diff --git a/res/images/indeterminate04.png b/res/images/indeterminate04.png deleted file mode 100644 index c7d5b4e04..000000000 Binary files a/res/images/indeterminate04.png and /dev/null differ diff --git a/res/images/indeterminate05.png b/res/images/indeterminate05.png deleted file mode 100644 index d6fb2a032..000000000 Binary files a/res/images/indeterminate05.png and /dev/null differ diff --git a/res/images/indeterminate06.png b/res/images/indeterminate06.png deleted file mode 100644 index 44867619f..000000000 Binary files a/res/images/indeterminate06.png and /dev/null differ diff --git a/screen_ui.cpp b/screen_ui.cpp index fd1a6c7fa..a72da58a6 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -69,19 +69,8 @@ ScreenRecoveryUI::ScreenRecoveryUI() : menu_top(0), menu_items(0), menu_sel(0), - - // These values are correct for the default image resources - // provided with the android platform. Devices which use - // different resources should have a subclass of ScreenRecoveryUI - // that overrides Init() to set these values appropriately and - // then call the superclass Init(). animation_fps(20), - indeterminate_frames(6), - installing_frames(7), - install_overlay_offset_x(13), - install_overlay_offset_y(190), - overlay_offset_x(-1), - overlay_offset_y(-1), + installing_frames(-1), stage(-1), max_stage(-1) { @@ -92,20 +81,6 @@ ScreenRecoveryUI::ScreenRecoveryUI() : self = this; } -// Draw the given frame over the installation overlay animation. The -// background is not cleared or draw with the base icon first; we -// assume that the frame already contains some other frame of the -// animation. Does nothing if no overlay animation is defined. -// Should only be called with updateMutex locked. -void ScreenRecoveryUI::draw_install_overlay_locked(int frame) { - if (installationOverlay == NULL || overlay_offset_x < 0) return; - gr_surface surface = installationOverlay[frame]; - int iconWidth = gr_get_width(surface); - int iconHeight = gr_get_height(surface); - gr_blit(surface, 0, 0, iconWidth, iconHeight, - overlay_offset_x, overlay_offset_y); -} - // Clear the screen and draw the currently selected background icon (if any). // Should only be called with updateMutex locked. void ScreenRecoveryUI::draw_background_locked(Icon icon) @@ -116,6 +91,9 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) if (icon) { gr_surface surface = backgroundIcon[icon]; + if (icon == INSTALLING_UPDATE || icon == ERASING) { + surface = installation[installingFrame]; + } gr_surface text_surface = backgroundText[icon]; int iconWidth = gr_get_width(surface); @@ -126,8 +104,8 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) int sh = (max_stage >= 0) ? stageHeight : 0; - int iconX = (gr_fb_width() - iconWidth) / 2; - int iconY = (gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2; + iconX = (gr_fb_width() - iconWidth) / 2; + iconY = (gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2; int textX = (gr_fb_width() - textWidth) / 2; int textY = ((gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2) + iconHeight + 40; @@ -144,10 +122,6 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) } } - if (icon == INSTALLING_UPDATE || icon == ERASING) { - draw_install_overlay_locked(installingFrame); - } - gr_color(255, 255, 255, 255); gr_texticon(textX, textY, text_surface); } @@ -160,7 +134,8 @@ void ScreenRecoveryUI::draw_progress_locked() if (currentIcon == ERROR) return; if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { - draw_install_overlay_locked(installingFrame); + gr_surface icon = installation[installingFrame]; + gr_blit(icon, 0, 0, gr_get_width(icon), gr_get_height(icon), iconX, iconY); } if (progressBarType != EMPTY) { @@ -197,18 +172,6 @@ void ScreenRecoveryUI::draw_progress_locked() } } } - - if (progressBarType == INDETERMINATE) { - static int frame = 0; - gr_blit(progressBarIndeterminate[frame], 0, 0, width, height, dx, dy); - // in RTL locales, we run the animation backwards, which - // makes the spinner spin the other way. - if (rtl_locale) { - frame = (frame + indeterminate_frames - 1) % indeterminate_frames; - } else { - frame = (frame + 1) % indeterminate_frames; - } - } } } @@ -335,12 +298,6 @@ void ScreenRecoveryUI::progress_loop() { redraw = 1; } - // update the progress bar animation, if active - // skip this if we have a text overlay (too expensive to update) - if (progressBarType == INDETERMINATE && !show_text) { - redraw = 1; - } - // move the progress bar forward on timed intervals, if configured int duration = progressScopeDuration; if (progressBarType == DETERMINATE && duration > 0) { @@ -371,6 +328,13 @@ void ScreenRecoveryUI::LoadBitmap(const char* filename, gr_surface* surface) { } } +void ScreenRecoveryUI::LoadBitmapArray(const char* filename, int* frames, gr_surface** surface) { + int result = res_create_multi_surface(filename, frames, surface); + if (result < 0) { + LOGE("missing bitmap %s\n(Code %d)\n", filename, result); + } +} + void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, gr_surface* surface) { int result = res_create_localized_surface(filename, surface); if (result < 0) { @@ -393,7 +357,8 @@ void ScreenRecoveryUI::Init() if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1; backgroundIcon[NONE] = NULL; - LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]); + LoadBitmapArray("icon_installing", &installing_frames, &installation); + backgroundIcon[INSTALLING_UPDATE] = installing_frames ? installation[0] : NULL; backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE]; LoadBitmap("icon_error", &backgroundIcon[ERROR]); backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR]; @@ -408,31 +373,6 @@ void ScreenRecoveryUI::Init() LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]); LoadLocalizedBitmap("error_text", &backgroundText[ERROR]); - int i; - - progressBarIndeterminate = (gr_surface*)malloc(indeterminate_frames * - sizeof(gr_surface)); - for (i = 0; i < indeterminate_frames; ++i) { - char filename[40]; - // "indeterminate01.png", "indeterminate02.png", ... - sprintf(filename, "indeterminate%02d", i+1); - LoadBitmap(filename, progressBarIndeterminate+i); - } - - if (installing_frames > 0) { - installationOverlay = (gr_surface*)malloc(installing_frames * - sizeof(gr_surface)); - for (i = 0; i < installing_frames; ++i) { - char filename[40]; - // "icon_installing_overlay01.png", - // "icon_installing_overlay02.png", ... - sprintf(filename, "icon_installing_overlay%02d", i+1); - LoadBitmap(filename, installationOverlay+i); - } - } else { - installationOverlay = NULL; - } - pthread_create(&progress_t, NULL, progress_thread, NULL); RecoveryUI::Init(); @@ -465,19 +405,6 @@ void ScreenRecoveryUI::SetBackground(Icon icon) { pthread_mutex_lock(&updateMutex); - // Adjust the offset to account for the positioning of the - // base image on the screen. - if (backgroundIcon[icon] != NULL) { - gr_surface bg = backgroundIcon[icon]; - gr_surface text = backgroundText[icon]; - overlay_offset_x = install_overlay_offset_x + (gr_fb_width() - gr_get_width(bg)) / 2; - overlay_offset_y = install_overlay_offset_y + - (gr_fb_height() - (gr_get_height(bg) + - gr_get_height(text) + - 40 + - ((max_stage >= 0) ? gr_get_height(stageMarkerEmpty) : 0))) / 2; - } - currentIcon = icon; update_screen_locked(); @@ -517,7 +444,7 @@ void ScreenRecoveryUI::SetProgress(float fraction) if (fraction > 1.0) fraction = 1.0; if (progressBarType == DETERMINATE && fraction > progress) { // Skip updates that aren't visibly different. - int width = gr_get_width(progressBarIndeterminate[0]); + int width = gr_get_width(progressBarEmpty); float scale = width * progressScopeSize; if ((int) (progress * scale) != (int) (fraction * scale)) { progress = fraction; diff --git a/screen_ui.h b/screen_ui.h index 5c4366d28..0221ff253 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -68,8 +68,7 @@ class ScreenRecoveryUI : public RecoveryUI { pthread_mutex_t updateMutex; gr_surface backgroundIcon[5]; gr_surface backgroundText[5]; - gr_surface *installationOverlay; - gr_surface *progressBarIndeterminate; + gr_surface *installation; gr_surface progressBarEmpty; gr_surface progressBarFill; gr_surface stageMarkerEmpty; @@ -101,12 +100,11 @@ class ScreenRecoveryUI : public RecoveryUI { pthread_t progress_t; int animation_fps; - int indeterminate_frames; int installing_frames; protected: - int install_overlay_offset_x, install_overlay_offset_y; private: - int overlay_offset_x, overlay_offset_y; + + int iconX, iconY; int stage, max_stage; @@ -120,6 +118,7 @@ class ScreenRecoveryUI : public RecoveryUI { void progress_loop(); void LoadBitmap(const char* filename, gr_surface* surface); + void LoadBitmapArray(const char* filename, int* frames, gr_surface** surface); void LoadLocalizedBitmap(const char* filename, gr_surface* surface); }; -- cgit v1.2.3 From 39cf417e17011a72dd39acfe4cc8c90af26bdbaf Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 6 Mar 2014 16:16:05 -0800 Subject: remove pixelflinger from recovery Recovery now draws directly to the framebuffer by rolling its own graphics code, rather than depending on libpixelflinger. The recovery UI is modified slightly to eliminate operations that are slow with the software implementation: when the text display / menu is turned on, it now appears on a black background instead of a dimmed version of the recovery icon. There's probably substantial room for optimization of the graphics operations. Bug: 12131110 Change-Id: Iab6520e0a7aaec39e2ce39377c10aef82ae0c595 --- Android.mk | 1 - minui/graphics.c | 499 ++++++++++++++++++++++++++++++------------------- minui/minui.h | 18 +- minui/resources.c | 103 ++++------ mtdutils/flash_image.c | 3 + screen_ui.cpp | 14 +- 6 files changed, 357 insertions(+), 281 deletions(-) diff --git a/Android.mk b/Android.mk index c910e7db7..929afd97d 100644 --- a/Android.mk +++ b/Android.mk @@ -46,7 +46,6 @@ LOCAL_STATIC_LIBRARIES := \ libmincrypt \ libminadbd \ libminui \ - libpixelflinger_static \ libpng \ libfs_mgr \ libcutils \ diff --git a/minui/graphics.c b/minui/graphics.c index a2014dca3..c2fc9ea9e 100644 --- a/minui/graphics.c +++ b/minui/graphics.c @@ -28,37 +28,19 @@ #include #include -#include +#include #include "font_10x18.h" #include "minui.h" -#if defined(RECOVERY_BGRA) -#define PIXEL_FORMAT GGL_PIXEL_FORMAT_BGRA_8888 -#define PIXEL_SIZE 4 -#elif defined(RECOVERY_RGBX) -#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGBX_8888 -#define PIXEL_SIZE 4 -#else -#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGB_565 -#define PIXEL_SIZE 2 -#endif - -#define NUM_BUFFERS 2 - typedef struct { - GGLSurface* texture; - unsigned cwidth; - unsigned cheight; + GRSurface* texture; + int cwidth; + int cheight; } GRFont; -static GRFont *gr_font = 0; -static GGLContext *gr_context = 0; -static GGLSurface gr_font_texture; -static GGLSurface gr_framebuffer[NUM_BUFFERS]; -static GGLSurface gr_mem_surface; -static unsigned gr_active_fb = 0; -static unsigned double_buffering = 0; +static GRFont* gr_font = NULL; + static int overscan_percent = OVERSCAN_PERCENT; static int overscan_offset_x = 0; static int overscan_offset_y = 0; @@ -66,67 +48,82 @@ static int overscan_offset_y = 0; static int gr_fb_fd = -1; static int gr_vt_fd = -1; +static GRSurface gr_framebuffer[2]; +static bool double_buffered; +static GRSurface* gr_draw = NULL; +static int displayed_buffer; + +static unsigned char gr_current_r = 255; +static unsigned char gr_current_g = 255; +static unsigned char gr_current_b = 255; +static unsigned char gr_current_a = 255; + static struct fb_var_screeninfo vi; -static struct fb_fix_screeninfo fi; -static int get_framebuffer(GGLSurface *fb) +static bool outside(int x, int y) +{ + return x < 0 || x >= gr_draw->width || y < 0 || y >= gr_draw->height; +} + +static void set_displayed_framebuffer(unsigned n) +{ + if (n > 1 || !double_buffered) return; + + vi.yres_virtual = gr_framebuffer[0].height * 2; + vi.yoffset = n * gr_framebuffer[0].height; + vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8; + if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { + perror("active fb swap failed"); + } + displayed_buffer = n; +} + +static int get_framebuffer() { int fd; void *bits; + struct fb_fix_screeninfo fi; + fd = open("/dev/graphics/fb0", O_RDWR); if (fd < 0) { perror("cannot open fb0"); return -1; } - if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { + if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { perror("failed to get fb0 info"); close(fd); return -1; } - vi.bits_per_pixel = PIXEL_SIZE * 8; - if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_BGRA_8888) { - vi.red.offset = 8; - vi.red.length = 8; - vi.green.offset = 16; - vi.green.length = 8; - vi.blue.offset = 24; - vi.blue.length = 8; - vi.transp.offset = 0; - vi.transp.length = 8; - } else if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_RGBX_8888) { - vi.red.offset = 24; - vi.red.length = 8; - vi.green.offset = 16; - vi.green.length = 8; - vi.blue.offset = 8; - vi.blue.length = 8; - vi.transp.offset = 0; - vi.transp.length = 8; - } else { /* RGB565*/ - vi.red.offset = 11; - vi.red.length = 5; - vi.green.offset = 5; - vi.green.length = 6; - vi.blue.offset = 0; - vi.blue.length = 5; - vi.transp.offset = 0; - vi.transp.length = 0; - } - if (ioctl(fd, FBIOPUT_VSCREENINFO, &vi) < 0) { - perror("failed to put fb0 info"); - close(fd); - return -1; - } - - if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { + if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { perror("failed to get fb0 info"); close(fd); return -1; } + // We print this out for informational purposes only, but + // throughout we assume that the framebuffer device uses an RGBX + // pixel format. This is the case for every development device I + // have access to. For some of those devices (eg, hammerhead aka + // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a + // different format (XBGR) but actually produces the correct + // results on the display when you write RGBX. + // + // If you have a device that actually *needs* another pixel format + // (ie, BGRX, or 565), patches welcome... + + printf("fb0 reports (possibly inaccurate):\n" + " vi.bits_per_pixel = %d\n" + " vi.red.offset = %3d .length = %3d\n" + " vi.green.offset = %3d .length = %3d\n" + " vi.blue.offset = %3d .length = %3d\n", + vi.bits_per_pixel, + vi.red.offset, vi.red.length, + vi.green.offset, vi.green.length, + vi.blue.offset, vi.blue.length); + bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (bits == MAP_FAILED) { perror("failed to mmap framebuffer"); @@ -137,79 +134,59 @@ static int get_framebuffer(GGLSurface *fb) overscan_offset_x = vi.xres * overscan_percent / 100; overscan_offset_y = vi.yres * overscan_percent / 100; - fb->version = sizeof(*fb); - fb->width = vi.xres; - fb->height = vi.yres; - fb->stride = fi.line_length/PIXEL_SIZE; - fb->data = bits; - fb->format = PIXEL_FORMAT; - memset(fb->data, 0, vi.yres * fi.line_length); - - fb++; + gr_framebuffer[0].width = vi.xres; + gr_framebuffer[0].height = vi.yres; + gr_framebuffer[0].row_bytes = fi.line_length; + gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8; + gr_framebuffer[0].data = bits; + memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes); /* check if we can use double buffering */ - if (vi.yres * fi.line_length * 2 > fi.smem_len) - return fd; + if (vi.yres * fi.line_length * 2 <= fi.smem_len) { + double_buffered = true; - double_buffering = 1; + memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface)); + gr_framebuffer[1].data = gr_framebuffer[0].data + + gr_framebuffer[0].height * gr_framebuffer[0].row_bytes; - fb->version = sizeof(*fb); - fb->width = vi.xres; - fb->height = vi.yres; - fb->stride = fi.line_length/PIXEL_SIZE; - fb->data = (void*) (((char*) bits) + vi.yres * fi.line_length); - fb->format = PIXEL_FORMAT; - memset(fb->data, 0, vi.yres * fi.line_length); - - return fd; -} - -static void get_memory_surface(GGLSurface* ms) { - ms->version = sizeof(*ms); - ms->width = vi.xres; - ms->height = vi.yres; - ms->stride = fi.line_length/PIXEL_SIZE; - ms->data = malloc(fi.line_length * vi.yres); - ms->format = PIXEL_FORMAT; -} + gr_draw = gr_framebuffer+1; -static void set_active_framebuffer(unsigned n) -{ - if (n > 1 || !double_buffering) return; - vi.yres_virtual = vi.yres * NUM_BUFFERS; - vi.yoffset = n * vi.yres; - vi.bits_per_pixel = PIXEL_SIZE * 8; - if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { - perror("active fb swap failed"); + } else { + double_buffered = false; + + // Without double-buffering, we allocate RAM for a buffer to + // draw in, and then "flipping" the buffer consists of a + // memcpy from the buffer we allocated to the framebuffer. + + gr_draw = (GRSurface*) malloc(sizeof(GRSurface)); + memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface)); + gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes); + if (!gr_draw->data) { + perror("failed to allocate in-memory surface"); + return -1; + } } -} -void gr_flip(void) -{ - GGLContext *gl = gr_context; - - /* swap front and back buffers */ - if (double_buffering) - gr_active_fb = (gr_active_fb + 1) & 1; + memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes); + gr_fb_fd = fd; + set_displayed_framebuffer(0); - /* copy data from the in-memory surface to the buffer we're about - * to make active. */ - memcpy(gr_framebuffer[gr_active_fb].data, gr_mem_surface.data, - fi.line_length * vi.yres); - - /* inform the display driver */ - set_active_framebuffer(gr_active_fb); + return fd; } -void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +void gr_flip(void) { - GGLContext *gl = gr_context; - GGLint color[4]; - color[0] = ((r << 8) | r) + 1; - color[1] = ((g << 8) | g) + 1; - color[2] = ((b << 8) | b) + 1; - color[3] = ((a << 8) | a) + 1; - gl->color4xv(gl, color); + if (double_buffered) { + // Change gr_draw to point to the buffer currently displayed, + // then flip the driver so we're displaying the other buffer + // instead. + gr_draw = gr_framebuffer + displayed_buffer; + set_displayed_framebuffer(1-displayed_buffer); + } else { + // Copy from the in-memory surface to the framebuffer. + memcpy(gr_framebuffer[0].data, gr_draw->data, + gr_draw->height * gr_draw->row_bytes); + } } int gr_measure(const char *s) @@ -223,58 +200,118 @@ void gr_font_size(int *x, int *y) *y = gr_font->cheight; } -int gr_text(int x, int y, const char *s, int bold) +static void text_blend(unsigned char* src_p, int src_row_bytes, + unsigned char* dst_p, int dst_row_bytes, + int width, int height) +{ + int i, j; + for (j = 0; j < height; ++j) { + unsigned char* sx = src_p; + unsigned char* px = dst_p; + for (i = 0; i < width; ++i) { + unsigned char a = *sx++; + if (gr_current_a < 255) a = ((int)a * gr_current_a) / 255; + if (a == 255) { + *px++ = gr_current_r; + *px++ = gr_current_g; + *px++ = gr_current_b; + px++; + } else if (a > 0) { + *px = (*px * (255-a) + gr_current_r * a) / 255; + ++px; + *px = (*px * (255-a) + gr_current_g * a) / 255; + ++px; + *px = (*px * (255-a) + gr_current_b * a) / 255; + ++px; + ++px; + } else { + px += 4; + } + } + src_p += src_row_bytes; + dst_p += dst_row_bytes; + } +} + + +void gr_text(int x, int y, const char *s, int bold) { - GGLContext *gl = gr_context; GRFont *font = gr_font; unsigned off; - if (!font->texture) return x; + if (!font->texture) return; + if (gr_current_a == 0) return; bold = bold && (font->texture->height != font->cheight); x += overscan_offset_x; y += overscan_offset_y; - gl->bindTexture(gl, font->texture); - gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); - gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - gl->enable(gl, GGL_TEXTURE_2D); - while((off = *s++)) { off -= 32; + if (outside(x, y) || outside(x+font->cwidth-1, y+font->cheight-1)) break; if (off < 96) { - gl->texCoord2i(gl, (off * font->cwidth) - x, - (bold ? font->cheight : 0) - y); - gl->recti(gl, x, y, x + font->cwidth, y + font->cheight); + + unsigned char* src_p = font->texture->data + (off * font->cwidth) + + (bold ? font->cheight * font->texture->row_bytes : 0); + unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes; + + text_blend(src_p, font->texture->row_bytes, + dst_p, gr_draw->row_bytes, + font->cwidth, font->cheight); + } x += font->cwidth; } - - return x; } -void gr_texticon(int x, int y, gr_surface icon) { - if (gr_context == NULL || icon == NULL) { +void gr_texticon(int x, int y, GRSurface* icon) { + if (icon == NULL) return; + + if (icon->pixel_bytes != 1) { + printf("gr_texticon: source has wrong format\n"); return; } - GGLContext* gl = gr_context; x += overscan_offset_x; y += overscan_offset_y; - gl->bindTexture(gl, (GGLSurface*) icon); - gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); - gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - gl->enable(gl, GGL_TEXTURE_2D); + if (outside(x, y) || outside(x+icon->width-1, y+icon->height-1)) return; + + unsigned char* src_p = icon->data; + unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes; - int w = gr_get_width(icon); - int h = gr_get_height(icon); + text_blend(src_p, icon->row_bytes, + dst_p, gr_draw->row_bytes, + icon->width, icon->height); +} - gl->texCoord2i(gl, -x, -y); - gl->recti(gl, x, y, x+gr_get_width(icon), y+gr_get_height(icon)); +void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + gr_current_r = r; + gr_current_g = g; + gr_current_b = b; + gr_current_a = a; +} + +void gr_clear() +{ + if (gr_current_r == gr_current_g && + gr_current_r == gr_current_b) { + memset(gr_draw->data, gr_current_r, gr_draw->height * gr_draw->row_bytes); + } else { + int x, y; + unsigned char* px = gr_draw->data; + for (y = 0; y < gr_draw->height; ++y) { + for (x = 0; x < gr_draw->width; ++x) { + *px++ = gr_current_r; + *px++ = gr_current_g; + *px++ = gr_current_b; + px++; + } + px += gr_draw->row_bytes - (gr_draw->width * gr_draw->pixel_bytes); + } + } } void gr_fill(int x1, int y1, int x2, int y2) @@ -285,48 +322,82 @@ void gr_fill(int x1, int y1, int x2, int y2) x2 += overscan_offset_x; y2 += overscan_offset_y; - GGLContext *gl = gr_context; - gl->disable(gl, GGL_TEXTURE_2D); - gl->recti(gl, x1, y1, x2, y2); + if (outside(x1, y1) || outside(x2-1, y2-1)) return; + + unsigned char* p = gr_draw->data + y1 * gr_draw->row_bytes + x1 * gr_draw->pixel_bytes; + if (gr_current_a == 255) { + int x, y; + for (y = y1; y < y2; ++y) { + unsigned char* px = p; + for (x = x1; x < x2; ++x) { + *px++ = gr_current_r; + *px++ = gr_current_g; + *px++ = gr_current_b; + px++; + } + p += gr_draw->row_bytes; + } + } else if (gr_current_a > 0) { + int x, y; + for (y = y1; y < y2; ++y) { + unsigned char* px = p; + for (x = x1; x < x2; ++x) { + *px = (*px * (255-gr_current_a) + gr_current_r * gr_current_a) / 255; + ++px; + *px = (*px * (255-gr_current_a) + gr_current_g * gr_current_a) / 255; + ++px; + *px = (*px * (255-gr_current_a) + gr_current_b * gr_current_a) / 255; + ++px; + ++px; + } + p += gr_draw->row_bytes; + } + } } -void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) { - if (gr_context == NULL || source == NULL) { +void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) { + if (source == NULL) return; + + if (gr_draw->pixel_bytes != source->pixel_bytes) { + printf("gr_blit: source has wrong format\n"); return; } - GGLContext *gl = gr_context; dx += overscan_offset_x; dy += overscan_offset_y; - gl->bindTexture(gl, (GGLSurface*) source); - gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); - gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - gl->enable(gl, GGL_TEXTURE_2D); - gl->texCoord2i(gl, sx - dx, sy - dy); - gl->recti(gl, dx, dy, dx + w, dy + h); + if (outside(dx, dy) || outside(dx+w-1, dy+h-1)) return; + + unsigned char* src_p = source->data + sy*source->row_bytes + sx*source->pixel_bytes; + unsigned char* dst_p = gr_draw->data + dy*gr_draw->row_bytes + dx*gr_draw->pixel_bytes; + + int i; + for (i = 0; i < h; ++i) { + memcpy(dst_p, src_p, w * source->pixel_bytes); + src_p += source->row_bytes; + dst_p += gr_draw->row_bytes; + } } -unsigned int gr_get_width(gr_surface surface) { +unsigned int gr_get_width(GRSurface* surface) { if (surface == NULL) { return 0; } - return ((GGLSurface*) surface)->width; + return surface->width; } -unsigned int gr_get_height(gr_surface surface) { +unsigned int gr_get_height(GRSurface* surface) { if (surface == NULL) { return 0; } - return ((GGLSurface*) surface)->height; + return surface->height; } static void gr_init_font(void) { gr_font = calloc(sizeof(*gr_font), 1); - int res = res_create_surface("font", (void**)&(gr_font->texture)); + int res = res_create_surface("font", &(gr_font->texture)); if (res == 0) { // The font image should be a 96x2 array of character images. The // columns are the printable ASCII characters 0x20 - 0x7f. The @@ -340,7 +411,8 @@ static void gr_init_font(void) gr_font->texture = malloc(sizeof(*gr_font->texture)); gr_font->texture->width = font.width; gr_font->texture->height = font.height; - gr_font->texture->stride = font.width; + gr_font->texture->row_bytes = font.width; + gr_font->texture->pixel_bytes = 1; unsigned char* bits = malloc(font.width * font.height); gr_font->texture->data = (void*) bits; @@ -355,17 +427,61 @@ static void gr_init_font(void) gr_font->cwidth = font.cwidth; gr_font->cheight = font.cheight; } +} - // interpret the grayscale as alpha - gr_font->texture->format = GGL_PIXEL_FORMAT_A_8; +#if 0 +// Exercises many of the gr_*() functions; useful for testing. +static void gr_test() { + GRSurface** images; + int frames; + int result = res_create_multi_surface("icon_installing", &frames, &images); + if (result < 0) { + printf("create surface %d\n", result); + gr_exit(); + return; + } + + time_t start = time(NULL); + int x; + for (x = 0; x <= 1200; ++x) { + if (x < 400) { + gr_color(0, 0, 0, 255); + } else { + gr_color(0, (x-400)%128, 0, 255); + } + gr_clear(); + + gr_color(255, 0, 0, 255); + gr_surface frame = images[x%frames]; + gr_blit(frame, 0, 0, frame->width, frame->height, x, 0); + + gr_color(255, 0, 0, 128); + gr_fill(400, 150, 600, 350); + + gr_color(255, 255, 255, 255); + gr_text(500, 225, "hello, world!", 0); + gr_color(255, 255, 0, 128); + gr_text(300+x, 275, "pack my box with five dozen liquor jugs", 1); + + gr_color(0, 0, 255, 128); + gr_fill(gr_draw->width - 200 - x, 300, gr_draw->width - x, 500); + + gr_flip(); + } + printf("getting end time\n"); + time_t end = time(NULL); + printf("got end time\n"); + printf("start %ld end %ld\n", (long)start, (long)end); + if (end > start) { + printf("%.2f fps\n", ((double)x) / (end-start)); + } } +#endif int gr_init(void) { - gglInit(&gr_context); - GGLContext *gl = gr_context; - gr_init_font(); + gr_vt_fd = open("/dev/tty0", O_RDWR | O_SYNC); if (gr_vt_fd < 0) { // This is non-fatal; post-Cupcake kernels don't have tty0. @@ -377,25 +493,16 @@ int gr_init(void) return -1; } - gr_fb_fd = get_framebuffer(gr_framebuffer); - if (gr_fb_fd < 0) { + if (get_framebuffer(&gr_framebuffer) < 0) { gr_exit(); return -1; } - get_memory_surface(&gr_mem_surface); - printf("framebuffer: fd %d (%d x %d)\n", - gr_fb_fd, gr_framebuffer[0].width, gr_framebuffer[0].height); - - /* start with 0 as front (displayed) and 1 as back (drawing) */ - gr_active_fb = 0; - set_active_framebuffer(0); - gl->colorBuffer(gl, &gr_mem_surface); + gr_fb_fd, gr_draw->width, gr_draw->height); - gl->activeTexture(gl, 0); - gl->enable(gl, GGL_BLEND); - gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA); + gr_flip(); + gr_flip(); gr_fb_blank(true); gr_fb_blank(false); @@ -408,26 +515,24 @@ void gr_exit(void) close(gr_fb_fd); gr_fb_fd = -1; - free(gr_mem_surface.data); - ioctl(gr_vt_fd, KDSETMODE, (void*) KD_TEXT); close(gr_vt_fd); gr_vt_fd = -1; + + if (!double_buffered) { + if (gr_draw) free(gr_draw->data); + free(gr_draw); + } } int gr_fb_width(void) { - return gr_framebuffer[0].width - 2*overscan_offset_x; + return gr_draw->width - 2*overscan_offset_x; } int gr_fb_height(void) { - return gr_framebuffer[0].height - 2*overscan_offset_y; -} - -gr_pixel *gr_fb_data(void) -{ - return (unsigned short *) gr_mem_surface.data; + return gr_draw->height - 2*overscan_offset_y; } void gr_fb_blank(bool blank) diff --git a/minui/minui.h b/minui/minui.h index 3250955c7..83c121e26 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -23,22 +23,30 @@ extern "C" { #endif -typedef void* gr_surface; -typedef unsigned short gr_pixel; +typedef struct { + int width; + int height; + int row_bytes; + int pixel_bytes; + unsigned char* data; +} GRSurface; + +typedef GRSurface* gr_surface; int gr_init(void); void gr_exit(void); int gr_fb_width(void); int gr_fb_height(void); -gr_pixel *gr_fb_data(void); + void gr_flip(void); void gr_fb_blank(bool blank); +void gr_clear(); // clear entire surface to current color void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a); void gr_fill(int x1, int y1, int x2, int y2); -int gr_text(int x, int y, const char *s, int bold); - void gr_texticon(int x, int y, gr_surface icon); +void gr_text(int x, int y, const char *s, int bold); +void gr_texticon(int x, int y, gr_surface icon); int gr_measure(const char *s); void gr_font_size(int *x, int *y); diff --git a/minui/resources.c b/minui/resources.c index 91b01eb14..df813cb1e 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -27,25 +27,26 @@ #include #include -#include - #include #include "minui.h" extern char* locale; -// 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 * y; +#define SURFACE_DATA_ALIGNMENT 8 + +static gr_surface malloc_surface(size_t data_size) { + unsigned char* temp = malloc(sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT); + if (temp == NULL) return NULL; + gr_surface surface = (gr_surface) temp; + surface->data = temp + sizeof(GRSurface) + + (SURFACE_DATA_ALIGNMENT - (sizeof(GRSurface) % SURFACE_DATA_ALIGNMENT)); + return surface; } int res_create_surface(const char* name, gr_surface* pSurface) { char resPath[256]; - GGLSurface* surface = NULL; + gr_surface surface = NULL; int result = 0; unsigned char header[8]; png_structp png_ptr = NULL; @@ -100,9 +101,8 @@ int res_create_surface(const char* name, gr_surface* pSurface) { int channels = png_get_channels(png_ptr, info_ptr); - if (!(bit_depth == 8 && + if (!(bit_depth <= 8 && ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) || - (channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) || (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY))))) { return -7; @@ -110,38 +110,20 @@ int res_create_surface(const char* name, gr_surface* pSurface) { } size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width; - size_t pixelSize = stride * height; - surface = malloc(sizeof(GGLSurface) + pixelSize); + surface = malloc_surface(stride * height); if (surface == NULL) { result = -8; goto exit; } - unsigned char* pData = (unsigned char*) (surface + 1); - surface->version = sizeof(GGLSurface); + unsigned char* pData = surface->data; surface->width = width; surface->height = height; - surface->stride = width; /* Yes, pixels, not bytes */ - surface->data = pData; - - if (channels == 3) { - surface->format = GGL_PIXEL_FORMAT_RGBX_8888; - } else if (color_type == PNG_COLOR_TYPE_PALETTE) { - surface->format = GGL_PIXEL_FORMAT_RGBA_8888; - } else if (channels == 1) { - surface->format = GGL_PIXEL_FORMAT_L_8; - } else { - surface->format = GGL_PIXEL_FORMAT_RGBA_8888; - } + surface->row_bytes = stride; + surface->pixel_bytes = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4); int alpha = (channels == 4); - if (color_type == PNG_COLOR_TYPE_PALETTE) { - png_set_palette_to_rgb(png_ptr); - } - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { - png_set_tRNS_to_alpha(png_ptr); - alpha = 1; - } + png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY) { alpha = 1; } @@ -196,7 +178,7 @@ int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurfac png_structp png_ptr = NULL; png_infop info_ptr = NULL; int i; - GGLSurface** surface = NULL; + gr_surface* surface = NULL; *pSurface = NULL; *frames = -1; @@ -248,9 +230,8 @@ int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurfac int channels = png_get_channels(png_ptr, info_ptr); - if (!(bit_depth == 8 && + if (!(bit_depth <= 8 && ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) || - (channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) || (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY))))) { return -7; @@ -279,38 +260,21 @@ int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurfac size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width; size_t pixelSize = stride * height / *frames; - surface = malloc(*frames * sizeof(GGLSurface*)); + surface = malloc(*frames * sizeof(gr_surface)); if (surface == NULL) { result = -8; goto exit; } for (i = 0; i < *frames; ++i) { - surface[i] = malloc(sizeof(GGLSurface) + pixelSize); - surface[i]->version = sizeof(GGLSurface); + surface[i] = malloc_surface(pixelSize); surface[i]->width = width; surface[i]->height = height / *frames; - surface[i]->stride = width; /* Yes, pixels, not bytes */ - surface[i]->data = (unsigned char*) (surface[i] + 1); - - if (channels == 3) { - surface[i]->format = GGL_PIXEL_FORMAT_RGBX_8888; - } else if (color_type == PNG_COLOR_TYPE_PALETTE) { - surface[i]->format = GGL_PIXEL_FORMAT_RGBA_8888; - } else if (channels == 1) { - surface[i]->format = GGL_PIXEL_FORMAT_L_8; - } else { - surface[i]->format = GGL_PIXEL_FORMAT_RGBA_8888; - } + surface[i]->row_bytes = stride; + surface[i]->pixel_bytes = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4); } int alpha = (channels == 4); - if (color_type == PNG_COLOR_TYPE_PALETTE) { - png_set_palette_to_rgb(png_ptr); - } - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { - png_set_tRNS_to_alpha(png_ptr); - alpha = 1; - } + png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY) { alpha = 1; } @@ -384,7 +348,7 @@ static int matches_locale(const char* loc) { int res_create_localized_surface(const char* name, gr_surface* pSurface) { char resPath[256]; - GGLSurface* surface = NULL; + gr_surface surface = NULL; int result = 0; unsigned char header[8]; png_structp png_ptr = NULL; @@ -438,12 +402,14 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) { &color_type, NULL, NULL, NULL); int channels = png_get_channels(png_ptr, info_ptr); - if (!(bit_depth == 8 && + if (!(bit_depth <= 8 && (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) { return -7; goto exit; } + png_set_expand(png_ptr); + unsigned char* row = malloc(width); png_uint_32 y; for (y = 0; y < height; ++y) { @@ -456,19 +422,17 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) { if (y+1+h >= height || matches_locale(loc)) { printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y); - surface = malloc(sizeof(GGLSurface)); + surface = malloc_surface(w*h); if (surface == NULL) { result = -8; goto exit; } - unsigned char* pData = malloc(w*h); + unsigned char* pData = surface->data; - surface->version = sizeof(GGLSurface); surface->width = w; surface->height = h; - surface->stride = w; /* Yes, pixels, not bytes */ - surface->data = pData; - surface->format = GGL_PIXEL_FORMAT_A_8; + surface->row_bytes = w; + surface->pixel_bytes = 1; int i; for (i = 0; i < h; ++i, ++y) { @@ -501,8 +465,5 @@ exit: } void res_free_surface(gr_surface surface) { - GGLSurface* pSurface = (GGLSurface*) surface; - if (pSurface) { - free(pSurface); - } + free(surface); } diff --git a/mtdutils/flash_image.c b/mtdutils/flash_image.c index a39d60001..5657dfc82 100644 --- a/mtdutils/flash_image.c +++ b/mtdutils/flash_image.c @@ -24,6 +24,9 @@ #include "cutils/log.h" #include "mtdutils.h" +#ifdef LOG_TAG +#undef LOG_TAG +#endif #define LOG_TAG "flash_image" #define HEADER_SIZE 2048 // size of header to compare for equality diff --git a/screen_ui.cpp b/screen_ui.cpp index a72da58a6..c87c00ce5 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -87,7 +87,7 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) { pagesIdentical = false; gr_color(0, 0, 0, 255); - gr_fill(0, 0, gr_fb_width(), gr_fb_height()); + gr_clear(); if (icon) { gr_surface surface = backgroundIcon[icon]; @@ -203,12 +203,12 @@ void ScreenRecoveryUI::SetColor(UIElement e) { // Should only be called with updateMutex locked. void ScreenRecoveryUI::draw_screen_locked() { - draw_background_locked(currentIcon); - draw_progress_locked(); - - if (show_text) { - SetColor(TEXT_FILL); - gr_fill(0, 0, gr_fb_width(), gr_fb_height()); + if (!show_text) { + draw_background_locked(currentIcon); + draw_progress_locked(); + } else { + gr_color(0, 0, 0, 255); + gr_clear(); int y = 0; int i = 0; -- cgit v1.2.3 From 5120c9fbb60d6625ec2588d77f13953884bb1a93 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 11 Mar 2014 12:39:33 -0700 Subject: update tools for making recovery images We no longer render animations as a base image with a possibly-partially-transparent overlay drawn over it, so delete the make-overlay.py tool. Now we represent them as series of images that are interlaced by row (with a special text chunk in the PNG file specifying the number of frames) so add the interlace-frames.py tool to make those. Change-Id: I79443f125f9c7d8d61cd09e3434745e0ef38893f --- interlace-frames.py | 53 +++++++++++++++++++++++++++ make-overlay.py | 102 ---------------------------------------------------- 2 files changed, 53 insertions(+), 102 deletions(-) create mode 100644 interlace-frames.py delete mode 100644 make-overlay.py diff --git a/interlace-frames.py b/interlace-frames.py new file mode 100644 index 000000000..243e565e7 --- /dev/null +++ b/interlace-frames.py @@ -0,0 +1,53 @@ +# Copyright (C) 2014 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. + +"""Script to take a set of frames (PNG files) for a recovery animation +and turn it into a single output image which contains the input frames +interlaced by row. Run with the names of all the input frames on the +command line, in order, followed by the name of the output file.""" + +import sys +try: + import Image + import PngImagePlugin +except ImportError: + print "This script requires the Python Imaging Library to be installed." + sys.exit(1) + +frames = [Image.open(fn).convert("RGB") for fn in sys.argv[1:-1]] +assert len(frames) > 0, "Must have at least one input frame." +sizes = set() +for fr in frames: + sizes.add(fr.size) + +assert len(sizes) == 1, "All input images must have the same size." +w, h = sizes.pop() +N = len(frames) + +out = Image.new("RGB", (w, h*N)) +for j in range(h): + for i in range(w): + for fn, f in enumerate(frames): + out.putpixel((i, j*N+fn), f.getpixel((i, j))) + +# When loading this image, the graphics library expects to find a text +# chunk that specifies how many frames this animation represents. If +# you post-process the output of this script with some kind of +# optimizer tool (eg pngcrush or zopflipng) make sure that your +# optimizer preserves this text chunk. + +meta = PngImagePlugin.PngInfo() +meta.add_text("Frames", str(N)) + +out.save(sys.argv[-1], pnginfo=meta) diff --git a/make-overlay.py b/make-overlay.py deleted file mode 100644 index 7f931b373..000000000 --- a/make-overlay.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (C) 2011 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. - -"""Script to take a set of frames (PNG files) for a recovery -"installing" icon animation and turn it into a base image plus a set -of overlays, as needed by the recovery UI code. Run with the names of -all the input frames on the command line, in order.""" - -import sys -try: - import Image -except ImportError: - print "This script requires the Python Imaging Library to be installed." - sys.exit(1) - -# Find the smallest box that contains all the pixels which change -# between images. - -print "reading", sys.argv[1] -base = Image.open(sys.argv[1]) - -minmini = base.size[0]-1 -maxmaxi = 0 -minminj = base.size[1]-1 -maxmaxj = 0 - -for top_name in sys.argv[2:]: - print "reading", top_name - top = Image.open(top_name) - - assert base.size == top.size - - mini = base.size[0]-1 - maxi = 0 - minj = base.size[1]-1 - maxj = 0 - - h, w = base.size - for j in range(w): - for i in range(h): - b = base.getpixel((i,j)) - t = top.getpixel((i,j)) - if b != t: - if i < mini: mini = i - if i > maxi: maxi = i - if j < minj: minj = j - if j > maxj: maxj = j - - minmini = min(minmini, mini) - maxmaxi = max(maxmaxi, maxi) - minminj = min(minminj, minj) - maxmaxj = max(maxmaxj, maxj) - -w = maxmaxi - minmini + 1 -h = maxmaxj - minminj + 1 - -# Now write out an image containing just that box, for each frame. - -for num, top_name in enumerate(sys.argv[1:]): - top = Image.open(top_name) - - out = Image.new("RGB", (w, h)) - for i in range(w): - for j in range(h): - t = top.getpixel((i+minmini, j+minminj)) - out.putpixel((i, j), t) - - fn = "icon_installing_overlay%02d.png" % (num+1,) - out.save(fn) - print "saved", fn - -# Write out the base icon, which is the first frame with that box -# blacked out (just to make the file smaller, since it's always -# displayed with one of the overlays on top of it). - -for i in range(w): - for j in range(h): - base.putpixel((i+minmini, j+minminj), (0, 0, 0)) -fn = "icon_installing.png" -base.save(fn) -print "saved", fn - -# The device_ui_init() function needs to tell the recovery UI the -# position of the overlay box. - -print -print "add this to your device_ui_init() function:" -print "-" * 40 -print " ui_parameters->install_overlay_offset_x = %d;" % (minmini,) -print " ui_parameters->install_overlay_offset_y = %d;" % (minminj,) -print "-" * 40 -- cgit v1.2.3 From 830b3e33634391e8f7264e5567acf0b510ea2d19 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 11 Mar 2014 13:22:04 -0700 Subject: separate fbdev-specific code out from minui Isolate the code that interacts with fbdev, in preparation for adding a new backend. Change-Id: I19105e9da1ca6408cebc110f7e2bb5abfb481ee9 --- minui/Android.mk | 2 +- minui/graphics.c | 166 ++++------------------------------------ minui/graphics.h | 49 ++++++++++++ minui/graphics_fbdev.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++ minui/minui.h | 2 + 5 files changed, 268 insertions(+), 152 deletions(-) create mode 100644 minui/graphics.h create mode 100644 minui/graphics_fbdev.c diff --git a/minui/Android.mk b/minui/Android.mk index 43e0ad33b..5cc84ac7a 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -1,7 +1,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := graphics.c events.c resources.c +LOCAL_SRC_FILES := graphics.c graphics_fbdev.c events.c resources.c LOCAL_C_INCLUDES +=\ external/libpng\ diff --git a/minui/graphics.c b/minui/graphics.c index c2fc9ea9e..f8ffa8941 100644 --- a/minui/graphics.c +++ b/minui/graphics.c @@ -32,6 +32,7 @@ #include "font_10x18.h" #include "minui.h" +#include "graphics.h" typedef struct { GRSurface* texture; @@ -40,155 +41,26 @@ typedef struct { } GRFont; static GRFont* gr_font = NULL; +static minui_backend* gr_backend = NULL; static int overscan_percent = OVERSCAN_PERCENT; static int overscan_offset_x = 0; static int overscan_offset_y = 0; -static int gr_fb_fd = -1; static int gr_vt_fd = -1; -static GRSurface gr_framebuffer[2]; -static bool double_buffered; -static GRSurface* gr_draw = NULL; -static int displayed_buffer; - static unsigned char gr_current_r = 255; static unsigned char gr_current_g = 255; static unsigned char gr_current_b = 255; static unsigned char gr_current_a = 255; -static struct fb_var_screeninfo vi; +static GRSurface* gr_draw = NULL; static bool outside(int x, int y) { return x < 0 || x >= gr_draw->width || y < 0 || y >= gr_draw->height; } -static void set_displayed_framebuffer(unsigned n) -{ - if (n > 1 || !double_buffered) return; - - vi.yres_virtual = gr_framebuffer[0].height * 2; - vi.yoffset = n * gr_framebuffer[0].height; - vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8; - if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { - perror("active fb swap failed"); - } - displayed_buffer = n; -} - -static int get_framebuffer() -{ - int fd; - void *bits; - - struct fb_fix_screeninfo fi; - - fd = open("/dev/graphics/fb0", O_RDWR); - if (fd < 0) { - perror("cannot open fb0"); - return -1; - } - - if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { - perror("failed to get fb0 info"); - close(fd); - return -1; - } - - if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { - perror("failed to get fb0 info"); - close(fd); - return -1; - } - - // We print this out for informational purposes only, but - // throughout we assume that the framebuffer device uses an RGBX - // pixel format. This is the case for every development device I - // have access to. For some of those devices (eg, hammerhead aka - // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a - // different format (XBGR) but actually produces the correct - // results on the display when you write RGBX. - // - // If you have a device that actually *needs* another pixel format - // (ie, BGRX, or 565), patches welcome... - - printf("fb0 reports (possibly inaccurate):\n" - " vi.bits_per_pixel = %d\n" - " vi.red.offset = %3d .length = %3d\n" - " vi.green.offset = %3d .length = %3d\n" - " vi.blue.offset = %3d .length = %3d\n", - vi.bits_per_pixel, - vi.red.offset, vi.red.length, - vi.green.offset, vi.green.length, - vi.blue.offset, vi.blue.length); - - bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (bits == MAP_FAILED) { - perror("failed to mmap framebuffer"); - close(fd); - return -1; - } - - overscan_offset_x = vi.xres * overscan_percent / 100; - overscan_offset_y = vi.yres * overscan_percent / 100; - - gr_framebuffer[0].width = vi.xres; - gr_framebuffer[0].height = vi.yres; - gr_framebuffer[0].row_bytes = fi.line_length; - gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8; - gr_framebuffer[0].data = bits; - memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes); - - /* check if we can use double buffering */ - if (vi.yres * fi.line_length * 2 <= fi.smem_len) { - double_buffered = true; - - memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface)); - gr_framebuffer[1].data = gr_framebuffer[0].data + - gr_framebuffer[0].height * gr_framebuffer[0].row_bytes; - - gr_draw = gr_framebuffer+1; - - } else { - double_buffered = false; - - // Without double-buffering, we allocate RAM for a buffer to - // draw in, and then "flipping" the buffer consists of a - // memcpy from the buffer we allocated to the framebuffer. - - gr_draw = (GRSurface*) malloc(sizeof(GRSurface)); - memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface)); - gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes); - if (!gr_draw->data) { - perror("failed to allocate in-memory surface"); - return -1; - } - } - - memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes); - gr_fb_fd = fd; - set_displayed_framebuffer(0); - - return fd; -} - -void gr_flip(void) -{ - if (double_buffered) { - // Change gr_draw to point to the buffer currently displayed, - // then flip the driver so we're displaying the other buffer - // instead. - gr_draw = gr_framebuffer + displayed_buffer; - set_displayed_framebuffer(1-displayed_buffer); - } else { - // Copy from the in-memory surface to the framebuffer. - memcpy(gr_framebuffer[0].data, gr_draw->data, - gr_draw->height * gr_draw->row_bytes); - } -} - int gr_measure(const char *s) { return gr_font->cwidth * strlen(s); @@ -466,7 +338,7 @@ static void gr_test() { gr_color(0, 0, 255, 128); gr_fill(gr_draw->width - 200 - x, 300, gr_draw->width - x, 500); - gr_flip(); + gr_draw = gr_backend->flip(gr_backend); } printf("getting end time\n"); time_t end = time(NULL); @@ -478,6 +350,10 @@ static void gr_test() { } #endif +void gr_flip() { + gr_draw = gr_backend->flip(gr_backend); +} + int gr_init(void) { gr_init_font(); @@ -493,36 +369,28 @@ int gr_init(void) return -1; } - if (get_framebuffer(&gr_framebuffer) < 0) { - gr_exit(); + gr_backend = open_fbdev(); + gr_draw = gr_backend->init(gr_backend); + if (gr_draw == NULL) { return -1; } - printf("framebuffer: fd %d (%d x %d)\n", - gr_fb_fd, gr_draw->width, gr_draw->height); + overscan_offset_x = gr_draw->width * overscan_percent / 100; + overscan_offset_y = gr_draw->height * overscan_percent / 100; gr_flip(); gr_flip(); - gr_fb_blank(true); - gr_fb_blank(false); - return 0; } void gr_exit(void) { - close(gr_fb_fd); - gr_fb_fd = -1; + gr_backend->exit(gr_backend); ioctl(gr_vt_fd, KDSETMODE, (void*) KD_TEXT); close(gr_vt_fd); gr_vt_fd = -1; - - if (!double_buffered) { - if (gr_draw) free(gr_draw->data); - free(gr_draw); - } } int gr_fb_width(void) @@ -537,9 +405,5 @@ int gr_fb_height(void) void gr_fb_blank(bool blank) { - int ret; - - ret = ioctl(gr_fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK); - if (ret < 0) - perror("ioctl(): blank"); + gr_backend->blank(gr_backend, blank); } diff --git a/minui/graphics.h b/minui/graphics.h new file mode 100644 index 000000000..94b89a554 --- /dev/null +++ b/minui/graphics.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 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 _GRAPHICS_H_ +#define _GRAPHICS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "minui.h" + +typedef struct minui_backend { + // Initializes the backend and returns a gr_surface to draw into. + gr_surface (*init)(struct minui_backend*); + + // Causes the current drawing surface (returned by the most recent + // call to flip() or init()) to be displayed, and returns a new + // drawing surface. + gr_surface (*flip)(struct minui_backend*); + + // Blank (or unblank) the screen. + void (*blank)(struct minui_backend*, bool); + + // Device cleanup when drawing is done. + void (*exit)(struct minui_backend*); +} minui_backend; + +minui_backend* open_fbdev(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/minui/graphics_fbdev.c b/minui/graphics_fbdev.c new file mode 100644 index 000000000..6a6330b22 --- /dev/null +++ b/minui/graphics_fbdev.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2014 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 +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include "minui.h" +#include "graphics.h" + +static gr_surface fbdev_init(minui_backend*); +static gr_surface fbdev_flip(minui_backend*); +static void fbdev_blank(minui_backend*, bool); +static void fbdev_exit(minui_backend*); + +static GRSurface gr_framebuffer[2]; +static bool double_buffered; +static GRSurface* gr_draw = NULL; +static int displayed_buffer; + +static struct fb_var_screeninfo vi; +static int fb_fd = -1; + +static minui_backend my_backend = { + .init = fbdev_init, + .flip = fbdev_flip, + .blank = fbdev_blank, + .exit = fbdev_exit, +}; + +minui_backend* open_fbdev() { + return &my_backend; +} + +static void fbdev_blank(minui_backend* backend, bool blank) +{ + int ret; + + ret = ioctl(fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK); + if (ret < 0) + perror("ioctl(): blank"); +} + +static void set_displayed_framebuffer(unsigned n) +{ + if (n > 1 || !double_buffered) return; + + vi.yres_virtual = gr_framebuffer[0].height * 2; + vi.yoffset = n * gr_framebuffer[0].height; + vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8; + if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { + perror("active fb swap failed"); + } + displayed_buffer = n; +} + +static gr_surface fbdev_init(minui_backend* backend) { + int fd; + void *bits; + + struct fb_fix_screeninfo fi; + + fd = open("/dev/graphics/fb0", O_RDWR); + if (fd < 0) { + perror("cannot open fb0"); + return NULL; + } + + if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { + perror("failed to get fb0 info"); + close(fd); + return NULL; + } + + if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { + perror("failed to get fb0 info"); + close(fd); + return NULL; + } + + // We print this out for informational purposes only, but + // throughout we assume that the framebuffer device uses an RGBX + // pixel format. This is the case for every development device I + // have access to. For some of those devices (eg, hammerhead aka + // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a + // different format (XBGR) but actually produces the correct + // results on the display when you write RGBX. + // + // If you have a device that actually *needs* another pixel format + // (ie, BGRX, or 565), patches welcome... + + printf("fb0 reports (possibly inaccurate):\n" + " vi.bits_per_pixel = %d\n" + " vi.red.offset = %3d .length = %3d\n" + " vi.green.offset = %3d .length = %3d\n" + " vi.blue.offset = %3d .length = %3d\n", + vi.bits_per_pixel, + vi.red.offset, vi.red.length, + vi.green.offset, vi.green.length, + vi.blue.offset, vi.blue.length); + + bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (bits == MAP_FAILED) { + perror("failed to mmap framebuffer"); + close(fd); + return NULL; + } + + gr_framebuffer[0].width = vi.xres; + gr_framebuffer[0].height = vi.yres; + gr_framebuffer[0].row_bytes = fi.line_length; + gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8; + gr_framebuffer[0].data = bits; + memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes); + + /* check if we can use double buffering */ + if (vi.yres * fi.line_length * 2 <= fi.smem_len) { + double_buffered = true; + + memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface)); + gr_framebuffer[1].data = gr_framebuffer[0].data + + gr_framebuffer[0].height * gr_framebuffer[0].row_bytes; + + gr_draw = gr_framebuffer+1; + + } else { + double_buffered = false; + + // Without double-buffering, we allocate RAM for a buffer to + // draw in, and then "flipping" the buffer consists of a + // memcpy from the buffer we allocated to the framebuffer. + + gr_draw = (GRSurface*) malloc(sizeof(GRSurface)); + memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface)); + gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes); + if (!gr_draw->data) { + perror("failed to allocate in-memory surface"); + return NULL; + } + } + + memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes); + fb_fd = fd; + set_displayed_framebuffer(0); + + printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height); + + fbdev_blank(backend, true); + fbdev_blank(backend, false); + + return gr_draw; +} + +static gr_surface fbdev_flip(minui_backend* backend) { + if (double_buffered) { + // Change gr_draw to point to the buffer currently displayed, + // then flip the driver so we're displaying the other buffer + // instead. + gr_draw = gr_framebuffer + displayed_buffer; + set_displayed_framebuffer(1-displayed_buffer); + } else { + // Copy from the in-memory surface to the framebuffer. + memcpy(gr_framebuffer[0].data, gr_draw->data, + gr_draw->height * gr_draw->row_bytes); + } + return gr_draw; +} + +static void fbdev_exit(minui_backend* backend) { + close(fb_fd); + fb_fd = -1; + + if (!double_buffered && gr_draw) { + free(gr_draw->data); + free(gr_draw); + } + gr_draw = NULL; +} diff --git a/minui/minui.h b/minui/minui.h index 83c121e26..b4b57e1db 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -17,6 +17,8 @@ #ifndef _MINUI_H_ #define _MINUI_H_ +#include + #include #ifdef __cplusplus -- cgit v1.2.3 From 469954fe3d7c3d729e500512ab911a037b90cc77 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 7 Mar 2014 09:21:25 -0800 Subject: change how recovery animation is implemented Instead of one 'base' installing image and a number of overlay images that are drawn on top of it, we represent the installing animation with one PNG that contains all the animation frames, interlaced by row. The PNG is expected to have a text chunk with the keyword 'Frames' and a value that's the number of frames (as an ascii string). This representation provides better compression, removes the need to subclass ScreenRecoveryUI just to change the position of the overlay or number of frames, and doesn't require gr_blit() to support an alpha channel. We also remove the 'indeterminate' progress bar used when wiping data and/or cache. The main animation serves the same purpose (showing that the device is still alive); the spinning progress bar has been redundant for a while. This changes the default recovery animation to include the antenna-wiggling and gear-turning that's used in the Nexus 5 recovery animation. Change-Id: I51930a76035ac09969a25472f4e572b289418729 Conflicts: screen_ui.cpp screen_ui.h --- minui/minui.h | 1 + minui/resources.c | 182 ++++++++++++++++++++++++++++++- res/images/icon_installing.png | Bin 25261 -> 118562 bytes res/images/icon_installing_overlay01.png | Bin 10095 -> 0 bytes res/images/icon_installing_overlay02.png | Bin 9990 -> 0 bytes res/images/icon_installing_overlay03.png | Bin 9782 -> 0 bytes res/images/icon_installing_overlay04.png | Bin 9817 -> 0 bytes res/images/icon_installing_overlay05.png | Bin 9863 -> 0 bytes res/images/icon_installing_overlay06.png | Bin 9944 -> 0 bytes res/images/icon_installing_overlay07.png | Bin 10062 -> 0 bytes res/images/indeterminate01.png | Bin 673 -> 0 bytes res/images/indeterminate02.png | Bin 687 -> 0 bytes res/images/indeterminate03.png | Bin 661 -> 0 bytes res/images/indeterminate04.png | Bin 665 -> 0 bytes res/images/indeterminate05.png | Bin 683 -> 0 bytes res/images/indeterminate06.png | Bin 676 -> 0 bytes screen_ui.cpp | 107 ++++-------------- screen_ui.h | 8 +- 18 files changed, 203 insertions(+), 95 deletions(-) delete mode 100644 res/images/icon_installing_overlay01.png delete mode 100644 res/images/icon_installing_overlay02.png delete mode 100644 res/images/icon_installing_overlay03.png delete mode 100644 res/images/icon_installing_overlay04.png delete mode 100644 res/images/icon_installing_overlay05.png delete mode 100644 res/images/icon_installing_overlay06.png delete mode 100644 res/images/icon_installing_overlay07.png delete mode 100644 res/images/indeterminate01.png delete mode 100644 res/images/indeterminate02.png delete mode 100644 res/images/indeterminate03.png delete mode 100644 res/images/indeterminate04.png delete mode 100644 res/images/indeterminate05.png delete mode 100644 res/images/indeterminate06.png diff --git a/minui/minui.h b/minui/minui.h index 1b8dd059b..5c0defc40 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -72,6 +72,7 @@ void ev_dispatch(void); // Returns 0 if no error, else negative. int res_create_surface(const char* name, gr_surface* pSurface); +int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurface); int res_create_localized_surface(const char* name, gr_surface* pSurface); void res_free_surface(gr_surface surface); diff --git a/minui/resources.c b/minui/resources.c index c0a9ccacb..5ad9b1d05 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -181,6 +181,182 @@ exit: return result; } +int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurface) { + char resPath[256]; + int result = 0; + unsigned char header[8]; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + int i; + GGLSurface** surface = NULL; + + *pSurface = NULL; + *frames = -1; + + snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name); + resPath[sizeof(resPath)-1] = '\0'; + FILE* fp = fopen(resPath, "rb"); + if (fp == NULL) { + result = -1; + goto exit; + } + + size_t bytesRead = fread(header, 1, sizeof(header), fp); + if (bytesRead != sizeof(header)) { + result = -2; + goto exit; + } + + if (png_sig_cmp(header, 0, sizeof(header))) { + result = -3; + goto exit; + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + result = -4; + goto exit; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + result = -5; + goto exit; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + result = -6; + goto exit; + } + + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, sizeof(header)); + png_read_info(png_ptr, info_ptr); + + int color_type, bit_depth; + png_uint_32 width, height; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, + &color_type, NULL, NULL, NULL); + + int channels = png_get_channels(png_ptr, info_ptr); + + if (!(bit_depth == 8 && + ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) || + (channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) || + (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE || + color_type == PNG_COLOR_TYPE_GRAY))))) { + return -7; + goto exit; + } + + *frames = 1; + png_textp text; + int num_text; + if (png_get_text(png_ptr, info_ptr, &text, &num_text)) { + for (i = 0; i < num_text; ++i) { + if (text[i].key && strcmp(text[i].key, "Frames") == 0 && text[i].text) { + *frames = atoi(text[i].text); + break; + } + } + printf(" found frames = %d\n", *frames); + } + + if (height % *frames != 0) { + printf("bad height (%d) for frame count (%d)\n", height, *frames); + result = -9; + goto exit; + } + + size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width; + size_t pixelSize = stride * height / *frames; + + surface = malloc(*frames * sizeof(GGLSurface*)); + if (surface == NULL) { + result = -8; + goto exit; + } + for (i = 0; i < *frames; ++i) { + surface[i] = malloc(sizeof(GGLSurface) + pixelSize); + surface[i]->version = sizeof(GGLSurface); + surface[i]->width = width; + surface[i]->height = height / *frames; + surface[i]->stride = width; /* Yes, pixels, not bytes */ + surface[i]->data = (unsigned char*) (surface[i] + 1); + + if (channels == 3) { + surface[i]->format = GGL_PIXEL_FORMAT_RGBX_8888; + } else if (color_type == PNG_COLOR_TYPE_PALETTE) { + surface[i]->format = GGL_PIXEL_FORMAT_RGBA_8888; + } else if (channels == 1) { + surface[i]->format = GGL_PIXEL_FORMAT_L_8; + } else { + surface[i]->format = GGL_PIXEL_FORMAT_RGBA_8888; + } + } + + int alpha = (channels == 4); + if (color_type == PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(png_ptr); + } + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(png_ptr); + alpha = 1; + } + if (color_type == PNG_COLOR_TYPE_GRAY) { + alpha = 1; + } + + png_uint_32 y; + if (channels == 3 || (channels == 1 && !alpha)) { + for (y = 0; y < height; ++y) { + int fy = y / *frames; + int fr = y % *frames; + unsigned char* pRow = surface[fr]->data + fy * 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 r = pRow[sx]; + unsigned char g = pRow[sx + 1]; + 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 + 3] = a; + } + } + } else { + for (y = 0; y < height; ++y) { + int fy = y / *frames; + int fr = y % *frames; + unsigned char* pRow = surface[fr]->data + fy * stride; + png_read_row(png_ptr, pRow, NULL); + } + } + + *pSurface = (gr_surface*) surface; + +exit: + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + if (fp != NULL) { + fclose(fp); + } + if (result < 0) { + if (surface) { + for (i = 0; i < *frames; ++i) { + if (surface[i]) free(surface[i]); + } + free(surface); + } + } + return result; +} + static int matches_locale(const char* loc) { if (locale == NULL) return 0; @@ -249,7 +425,7 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) { png_read_info(png_ptr, info_ptr); int color_type, bit_depth; - size_t width, height; + png_uint_32 width, height; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); int channels = png_get_channels(png_ptr, info_ptr); @@ -261,13 +437,13 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) { } unsigned char* row = malloc(width); - int y; + png_uint_32 y; for (y = 0; y < height; ++y) { png_read_row(png_ptr, row, NULL); int w = (row[1] << 8) | row[0]; int h = (row[3] << 8) | row[2]; int len = row[4]; - char* loc = row+5; + char* loc = (char*)row+5; if (y+1+h >= height || matches_locale(loc)) { printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y); diff --git a/res/images/icon_installing.png b/res/images/icon_installing.png index 571eb8b0f..c2c020162 100644 Binary files a/res/images/icon_installing.png and b/res/images/icon_installing.png differ diff --git a/res/images/icon_installing_overlay01.png b/res/images/icon_installing_overlay01.png deleted file mode 100644 index e762d6cbe..000000000 Binary files a/res/images/icon_installing_overlay01.png and /dev/null differ diff --git a/res/images/icon_installing_overlay02.png b/res/images/icon_installing_overlay02.png deleted file mode 100644 index f7a853017..000000000 Binary files a/res/images/icon_installing_overlay02.png and /dev/null differ diff --git a/res/images/icon_installing_overlay03.png b/res/images/icon_installing_overlay03.png deleted file mode 100644 index 1a1d738e4..000000000 Binary files a/res/images/icon_installing_overlay03.png and /dev/null differ diff --git a/res/images/icon_installing_overlay04.png b/res/images/icon_installing_overlay04.png deleted file mode 100644 index a74903d33..000000000 Binary files a/res/images/icon_installing_overlay04.png and /dev/null differ diff --git a/res/images/icon_installing_overlay05.png b/res/images/icon_installing_overlay05.png deleted file mode 100644 index d17bdc006..000000000 Binary files a/res/images/icon_installing_overlay05.png and /dev/null differ diff --git a/res/images/icon_installing_overlay06.png b/res/images/icon_installing_overlay06.png deleted file mode 100644 index 1200b75cb..000000000 Binary files a/res/images/icon_installing_overlay06.png and /dev/null differ diff --git a/res/images/icon_installing_overlay07.png b/res/images/icon_installing_overlay07.png deleted file mode 100644 index 3838a85ad..000000000 Binary files a/res/images/icon_installing_overlay07.png and /dev/null differ diff --git a/res/images/indeterminate01.png b/res/images/indeterminate01.png deleted file mode 100644 index 933528d6d..000000000 Binary files a/res/images/indeterminate01.png and /dev/null differ diff --git a/res/images/indeterminate02.png b/res/images/indeterminate02.png deleted file mode 100644 index d760e2bdd..000000000 Binary files a/res/images/indeterminate02.png and /dev/null differ diff --git a/res/images/indeterminate03.png b/res/images/indeterminate03.png deleted file mode 100644 index 0e97399d1..000000000 Binary files a/res/images/indeterminate03.png and /dev/null differ diff --git a/res/images/indeterminate04.png b/res/images/indeterminate04.png deleted file mode 100644 index c7d5b4e04..000000000 Binary files a/res/images/indeterminate04.png and /dev/null differ diff --git a/res/images/indeterminate05.png b/res/images/indeterminate05.png deleted file mode 100644 index d6fb2a032..000000000 Binary files a/res/images/indeterminate05.png and /dev/null differ diff --git a/res/images/indeterminate06.png b/res/images/indeterminate06.png deleted file mode 100644 index 44867619f..000000000 Binary files a/res/images/indeterminate06.png and /dev/null differ diff --git a/screen_ui.cpp b/screen_ui.cpp index 8376341c3..38b21f84c 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -69,20 +69,8 @@ ScreenRecoveryUI::ScreenRecoveryUI() : menu_top(0), menu_items(0), menu_sel(0), - - // These values are correct for the default image resources - // provided with the android platform. Devices which use - // different resources should have a subclass of ScreenRecoveryUI - // that overrides Init() to set these values appropriately and - // then call the superclass Init(). animation_fps(20), - indeterminate_frames(6), - installing_frames(7), - install_overlay_offset_x(13), - install_overlay_offset_y(190), - overlay_offset_x(-1), - overlay_offset_y(-1) { - + installing_frames(-1) { for (int i = 0; i < 5; i++) backgroundIcon[i] = NULL; @@ -90,20 +78,6 @@ ScreenRecoveryUI::ScreenRecoveryUI() : self = this; } -// Draw the given frame over the installation overlay animation. The -// background is not cleared or draw with the base icon first; we -// assume that the frame already contains some other frame of the -// animation. Does nothing if no overlay animation is defined. -// Should only be called with updateMutex locked. -void ScreenRecoveryUI::draw_install_overlay_locked(int frame) { - if (installationOverlay == NULL || overlay_offset_x < 0) return; - gr_surface surface = installationOverlay[frame]; - int iconWidth = gr_get_width(surface); - int iconHeight = gr_get_height(surface); - gr_blit(surface, 0, 0, iconWidth, iconHeight, - overlay_offset_x, overlay_offset_y); -} - // Clear the screen and draw the currently selected background icon (if any). // Should only be called with updateMutex locked. void ScreenRecoveryUI::draw_background_locked(Icon icon) @@ -114,6 +88,9 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) if (icon) { gr_surface surface = backgroundIcon[icon]; + if (icon == INSTALLING_UPDATE || icon == ERASING) { + surface = installation[installingFrame]; + } gr_surface text_surface = backgroundText[icon]; int iconWidth = gr_get_width(surface); @@ -121,16 +98,13 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) int textWidth = gr_get_width(text_surface); int textHeight = gr_get_height(text_surface); - int iconX = (gr_fb_width() - iconWidth) / 2; - int iconY = (gr_fb_height() - (iconHeight+textHeight+40)) / 2; + iconX = (gr_fb_width() - iconWidth) / 2; + iconY = (gr_fb_height() - (iconHeight+textHeight+40)) / 2; int textX = (gr_fb_width() - textWidth) / 2; int textY = ((gr_fb_height() - (iconHeight+textHeight+40)) / 2) + iconHeight + 40; gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY); - if (icon == INSTALLING_UPDATE || icon == ERASING) { - draw_install_overlay_locked(installingFrame); - } gr_color(255, 255, 255, 255); gr_texticon(textX, textY, text_surface); @@ -144,7 +118,8 @@ void ScreenRecoveryUI::draw_progress_locked() if (currentIcon == ERROR) return; if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { - draw_install_overlay_locked(installingFrame); + gr_surface icon = installation[installingFrame]; + gr_blit(icon, 0, 0, gr_get_width(icon), gr_get_height(icon), iconX, iconY); } if (progressBarType != EMPTY) { @@ -181,18 +156,6 @@ void ScreenRecoveryUI::draw_progress_locked() } } } - - if (progressBarType == INDETERMINATE) { - static int frame = 0; - gr_blit(progressBarIndeterminate[frame], 0, 0, width, height, dx, dy); - // in RTL locales, we run the animation backwards, which - // makes the spinner spin the other way. - if (rtl_locale) { - frame = (frame + indeterminate_frames - 1) % indeterminate_frames; - } else { - frame = (frame + 1) % indeterminate_frames; - } - } } } @@ -319,12 +282,6 @@ void ScreenRecoveryUI::progress_loop() { redraw = 1; } - // update the progress bar animation, if active - // skip this if we have a text overlay (too expensive to update) - if (progressBarType == INDETERMINATE && !show_text) { - redraw = 1; - } - // move the progress bar forward on timed intervals, if configured int duration = progressScopeDuration; if (progressBarType == DETERMINATE && duration > 0) { @@ -355,6 +312,13 @@ void ScreenRecoveryUI::LoadBitmap(const char* filename, gr_surface* surface) { } } +void ScreenRecoveryUI::LoadBitmapArray(const char* filename, int* frames, gr_surface** surface) { + int result = res_create_multi_surface(filename, frames, surface); + if (result < 0) { + LOGE("missing bitmap %s\n(Code %d)\n", filename, result); + } +} + void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, gr_surface* surface) { int result = res_create_localized_surface(filename, surface); if (result < 0) { @@ -376,7 +340,9 @@ void ScreenRecoveryUI::Init() text_cols = gr_fb_width() / char_width; if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1; - LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]); + backgroundIcon[NONE] = NULL; + LoadBitmapArray("icon_installing", &installing_frames, &installation); + backgroundIcon[INSTALLING_UPDATE] = installing_frames ? installation[0] : NULL; backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE]; LoadBitmap("icon_error", &backgroundIcon[ERROR]); backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR]; @@ -389,31 +355,6 @@ void ScreenRecoveryUI::Init() LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]); LoadLocalizedBitmap("error_text", &backgroundText[ERROR]); - int i; - - progressBarIndeterminate = (gr_surface*)malloc(indeterminate_frames * - sizeof(gr_surface)); - for (i = 0; i < indeterminate_frames; ++i) { - char filename[40]; - // "indeterminate01.png", "indeterminate02.png", ... - sprintf(filename, "indeterminate%02d", i+1); - LoadBitmap(filename, progressBarIndeterminate+i); - } - - if (installing_frames > 0) { - installationOverlay = (gr_surface*)malloc(installing_frames * - sizeof(gr_surface)); - for (i = 0; i < installing_frames; ++i) { - char filename[40]; - // "icon_installing_overlay01.png", - // "icon_installing_overlay02.png", ... - sprintf(filename, "icon_installing_overlay%02d", i+1); - LoadBitmap(filename, installationOverlay+i); - } - } else { - installationOverlay = NULL; - } - pthread_create(&progress_t, NULL, progress_thread, NULL); RecoveryUI::Init(); @@ -446,16 +387,6 @@ void ScreenRecoveryUI::SetBackground(Icon icon) { pthread_mutex_lock(&updateMutex); - // Adjust the offset to account for the positioning of the - // base image on the screen. - if (backgroundIcon[icon] != NULL) { - gr_surface bg = backgroundIcon[icon]; - gr_surface text = backgroundText[icon]; - overlay_offset_x = install_overlay_offset_x + (gr_fb_width() - gr_get_width(bg)) / 2; - overlay_offset_y = install_overlay_offset_y + - (gr_fb_height() - (gr_get_height(bg) + gr_get_height(text) + 40)) / 2; - } - currentIcon = icon; update_screen_locked(); @@ -495,7 +426,7 @@ void ScreenRecoveryUI::SetProgress(float fraction) if (fraction > 1.0) fraction = 1.0; if (progressBarType == DETERMINATE && fraction > progress) { // Skip updates that aren't visibly different. - int width = gr_get_width(progressBarIndeterminate[0]); + int width = gr_get_width(progressBarEmpty); float scale = width * progressScopeSize; if ((int) (progress * scale) != (int) (fraction * scale)) { progress = fraction; diff --git a/screen_ui.h b/screen_ui.h index fc35d95b6..ada006d1e 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -69,8 +69,7 @@ class ScreenRecoveryUI : public RecoveryUI { pthread_mutex_t updateMutex; gr_surface backgroundIcon[5]; gr_surface backgroundText[5]; - gr_surface *installationOverlay; - gr_surface *progressBarIndeterminate; + gr_surface *installation; gr_surface progressBarEmpty; gr_surface progressBarFill; @@ -100,9 +99,9 @@ class ScreenRecoveryUI : public RecoveryUI { pthread_t progress_t; int animation_fps; - int indeterminate_frames; int installing_frames; - int overlay_offset_x, overlay_offset_y; + + int iconX, iconY; void draw_install_overlay_locked(int frame); void draw_background_locked(Icon icon); @@ -114,6 +113,7 @@ class ScreenRecoveryUI : public RecoveryUI { void progress_loop(); void LoadBitmap(const char* filename, gr_surface* surface); + void LoadBitmapArray(const char* filename, int* frames, gr_surface** surface); void LoadLocalizedBitmap(const char* filename, gr_surface* surface); }; -- cgit v1.2.3 From 4db31d20c92d093e1bdc9499aeeddc1adc19b209 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 4 Sep 2013 13:44:38 -0700 Subject: allow CheckKey to request mounting /system Also provide a default implementation of CheckKey that's reasonable for many devices (those that have power and volume keys). Change-Id: Icf6c7746ebd866152d402059dbd27fd16bd51ff8 --- Android.mk | 1 + ui.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- ui.h | 6 +++++- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/Android.mk b/Android.mk index 3d6156819..1308066df 100644 --- a/Android.mk +++ b/Android.mk @@ -89,6 +89,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := verifier_test LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_TAGS := tests +LOCAL_CFLAGS += -DNO_RECOVERY_MOUNT LOCAL_SRC_FILES := \ verifier_test.cpp \ asn1_decoder.cpp \ diff --git a/ui.cpp b/ui.cpp index cece02d31..5043ee528 100644 --- a/ui.cpp +++ b/ui.cpp @@ -31,6 +31,7 @@ #include #include "common.h" +#include "roots.h" #include "device.h" #include "minui/minui.h" #include "screen_ui.h" @@ -47,7 +48,10 @@ RecoveryUI::RecoveryUI() : key_queue_len(0), key_last_down(-1), key_long_press(false), - key_down_count(0) { + key_down_count(0), + consecutive_power_keys(0), + consecutive_alternate_keys(0), + last_key(-1) { pthread_mutex_init(&key_queue_mutex, NULL); pthread_cond_init(&key_queue_cond, NULL); self = this; @@ -152,6 +156,13 @@ void RecoveryUI::process_key(int key_code, int updown) { case RecoveryUI::ENQUEUE: EnqueueKey(key_code); break; + + case RecoveryUI::MOUNT_SYSTEM: +#ifndef NO_RECOVERY_MOUNT + ensure_path_mounted("/system"); + Print("Mounted /system."); +#endif + break; } } } @@ -258,8 +269,41 @@ void RecoveryUI::FlushKeys() { pthread_mutex_unlock(&key_queue_mutex); } +// The default CheckKey implementation assumes the device has power, +// volume up, and volume down keys. +// +// - Hold power and press vol-up to toggle display. +// - Press power seven times in a row to reboot. +// - Alternate vol-up and vol-down seven times to mount /system. RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) { - return RecoveryUI::ENQUEUE; + if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) { + return TOGGLE; + } + + if (key == KEY_POWER) { + ++consecutive_power_keys; + if (consecutive_power_keys >= 7) { + return REBOOT; + } + } else { + consecutive_power_keys = 0; + } + + if ((key == KEY_VOLUMEUP && + (last_key == KEY_VOLUMEDOWN || last_key == -1)) || + (key == KEY_VOLUMEDOWN && + (last_key == KEY_VOLUMEUP || last_key == -1))) { + ++consecutive_alternate_keys; + if (consecutive_alternate_keys >= 7) { + consecutive_alternate_keys = 0; + return MOUNT_SYSTEM; + } + } else { + consecutive_alternate_keys = 0; + } + last_key = key; + + return ENQUEUE; } void RecoveryUI::NextCheckKeyIsLong(bool is_long_press) { diff --git a/ui.h b/ui.h index 6c8987a33..d85fc659b 100644 --- a/ui.h +++ b/ui.h @@ -77,7 +77,7 @@ class RecoveryUI { // Return value indicates whether an immediate operation should be // triggered (toggling the display, rebooting the device), or if // the key should be enqueued for use by the main thread. - enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE }; + enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE, MOUNT_SYSTEM }; virtual KeyAction CheckKey(int key); // Called immediately before each call to CheckKey(), tell you if @@ -121,6 +121,10 @@ private: int key_down_count; // under key_queue_mutex int rel_sum; + int consecutive_power_keys; + int consecutive_alternate_keys; + int last_key; + typedef struct { RecoveryUI* ui; int key_code; -- cgit v1.2.3 From 3ceeb580eb47419ce1c74d54c226a1a70ff2332f Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 11 Mar 2014 12:39:33 -0700 Subject: update tools for making recovery images We no longer render animations as a base image with a possibly-partially-transparent overlay drawn over it, so delete the make-overlay.py tool. Now we represent them as series of images that are interlaced by row (with a special text chunk in the PNG file specifying the number of frames) so add the interlace-frames.py tool to make those. Change-Id: I866db269107a21351c3df3b4683f233f72234334 --- interlace-frames.py | 53 +++++++++++++++++++++++++++ make-overlay.py | 102 ---------------------------------------------------- 2 files changed, 53 insertions(+), 102 deletions(-) create mode 100644 interlace-frames.py delete mode 100644 make-overlay.py diff --git a/interlace-frames.py b/interlace-frames.py new file mode 100644 index 000000000..243e565e7 --- /dev/null +++ b/interlace-frames.py @@ -0,0 +1,53 @@ +# Copyright (C) 2014 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. + +"""Script to take a set of frames (PNG files) for a recovery animation +and turn it into a single output image which contains the input frames +interlaced by row. Run with the names of all the input frames on the +command line, in order, followed by the name of the output file.""" + +import sys +try: + import Image + import PngImagePlugin +except ImportError: + print "This script requires the Python Imaging Library to be installed." + sys.exit(1) + +frames = [Image.open(fn).convert("RGB") for fn in sys.argv[1:-1]] +assert len(frames) > 0, "Must have at least one input frame." +sizes = set() +for fr in frames: + sizes.add(fr.size) + +assert len(sizes) == 1, "All input images must have the same size." +w, h = sizes.pop() +N = len(frames) + +out = Image.new("RGB", (w, h*N)) +for j in range(h): + for i in range(w): + for fn, f in enumerate(frames): + out.putpixel((i, j*N+fn), f.getpixel((i, j))) + +# When loading this image, the graphics library expects to find a text +# chunk that specifies how many frames this animation represents. If +# you post-process the output of this script with some kind of +# optimizer tool (eg pngcrush or zopflipng) make sure that your +# optimizer preserves this text chunk. + +meta = PngImagePlugin.PngInfo() +meta.add_text("Frames", str(N)) + +out.save(sys.argv[-1], pnginfo=meta) diff --git a/make-overlay.py b/make-overlay.py deleted file mode 100644 index 7f931b373..000000000 --- a/make-overlay.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (C) 2011 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. - -"""Script to take a set of frames (PNG files) for a recovery -"installing" icon animation and turn it into a base image plus a set -of overlays, as needed by the recovery UI code. Run with the names of -all the input frames on the command line, in order.""" - -import sys -try: - import Image -except ImportError: - print "This script requires the Python Imaging Library to be installed." - sys.exit(1) - -# Find the smallest box that contains all the pixels which change -# between images. - -print "reading", sys.argv[1] -base = Image.open(sys.argv[1]) - -minmini = base.size[0]-1 -maxmaxi = 0 -minminj = base.size[1]-1 -maxmaxj = 0 - -for top_name in sys.argv[2:]: - print "reading", top_name - top = Image.open(top_name) - - assert base.size == top.size - - mini = base.size[0]-1 - maxi = 0 - minj = base.size[1]-1 - maxj = 0 - - h, w = base.size - for j in range(w): - for i in range(h): - b = base.getpixel((i,j)) - t = top.getpixel((i,j)) - if b != t: - if i < mini: mini = i - if i > maxi: maxi = i - if j < minj: minj = j - if j > maxj: maxj = j - - minmini = min(minmini, mini) - maxmaxi = max(maxmaxi, maxi) - minminj = min(minminj, minj) - maxmaxj = max(maxmaxj, maxj) - -w = maxmaxi - minmini + 1 -h = maxmaxj - minminj + 1 - -# Now write out an image containing just that box, for each frame. - -for num, top_name in enumerate(sys.argv[1:]): - top = Image.open(top_name) - - out = Image.new("RGB", (w, h)) - for i in range(w): - for j in range(h): - t = top.getpixel((i+minmini, j+minminj)) - out.putpixel((i, j), t) - - fn = "icon_installing_overlay%02d.png" % (num+1,) - out.save(fn) - print "saved", fn - -# Write out the base icon, which is the first frame with that box -# blacked out (just to make the file smaller, since it's always -# displayed with one of the overlays on top of it). - -for i in range(w): - for j in range(h): - base.putpixel((i+minmini, j+minminj), (0, 0, 0)) -fn = "icon_installing.png" -base.save(fn) -print "saved", fn - -# The device_ui_init() function needs to tell the recovery UI the -# position of the overlay box. - -print -print "add this to your device_ui_init() function:" -print "-" * 40 -print " ui_parameters->install_overlay_offset_x = %d;" % (minmini,) -print " ui_parameters->install_overlay_offset_y = %d;" % (minminj,) -print "-" * 40 -- cgit v1.2.3 From 16f97c3961f08e5db7930d99e592f0a9f752df46 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 6 Mar 2014 16:16:05 -0800 Subject: remove pixelflinger from recovery Recovery now draws directly to the framebuffer by rolling its own graphics code, rather than depending on libpixelflinger. The recovery UI is modified slightly to eliminate operations that are slow with the software implementation: when the text display / menu is turned on, it now appears on a black background instead of a dimmed version of the recovery icon. There's probably substantial room for optimization of the graphics operations. Bug: 12131110 Change-Id: Iab6520e0a7aaec39e2ce39377c10aef82ae0c595 Conflicts: minui/resources.c --- Android.mk | 1 - minui/graphics.c | 499 ++++++++++++++++++++++++++++++------------------- minui/minui.h | 18 +- minui/resources.c | 97 ++++------ mtdutils/flash_image.c | 3 + screen_ui.cpp | 14 +- 6 files changed, 358 insertions(+), 274 deletions(-) diff --git a/Android.mk b/Android.mk index 1308066df..e51862c00 100644 --- a/Android.mk +++ b/Android.mk @@ -45,7 +45,6 @@ LOCAL_STATIC_LIBRARIES := \ libmincrypt \ libminadbd \ libminui \ - libpixelflinger_static \ libpng \ libfs_mgr \ libcutils \ diff --git a/minui/graphics.c b/minui/graphics.c index a2014dca3..c2fc9ea9e 100644 --- a/minui/graphics.c +++ b/minui/graphics.c @@ -28,37 +28,19 @@ #include #include -#include +#include #include "font_10x18.h" #include "minui.h" -#if defined(RECOVERY_BGRA) -#define PIXEL_FORMAT GGL_PIXEL_FORMAT_BGRA_8888 -#define PIXEL_SIZE 4 -#elif defined(RECOVERY_RGBX) -#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGBX_8888 -#define PIXEL_SIZE 4 -#else -#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGB_565 -#define PIXEL_SIZE 2 -#endif - -#define NUM_BUFFERS 2 - typedef struct { - GGLSurface* texture; - unsigned cwidth; - unsigned cheight; + GRSurface* texture; + int cwidth; + int cheight; } GRFont; -static GRFont *gr_font = 0; -static GGLContext *gr_context = 0; -static GGLSurface gr_font_texture; -static GGLSurface gr_framebuffer[NUM_BUFFERS]; -static GGLSurface gr_mem_surface; -static unsigned gr_active_fb = 0; -static unsigned double_buffering = 0; +static GRFont* gr_font = NULL; + static int overscan_percent = OVERSCAN_PERCENT; static int overscan_offset_x = 0; static int overscan_offset_y = 0; @@ -66,67 +48,82 @@ static int overscan_offset_y = 0; static int gr_fb_fd = -1; static int gr_vt_fd = -1; +static GRSurface gr_framebuffer[2]; +static bool double_buffered; +static GRSurface* gr_draw = NULL; +static int displayed_buffer; + +static unsigned char gr_current_r = 255; +static unsigned char gr_current_g = 255; +static unsigned char gr_current_b = 255; +static unsigned char gr_current_a = 255; + static struct fb_var_screeninfo vi; -static struct fb_fix_screeninfo fi; -static int get_framebuffer(GGLSurface *fb) +static bool outside(int x, int y) +{ + return x < 0 || x >= gr_draw->width || y < 0 || y >= gr_draw->height; +} + +static void set_displayed_framebuffer(unsigned n) +{ + if (n > 1 || !double_buffered) return; + + vi.yres_virtual = gr_framebuffer[0].height * 2; + vi.yoffset = n * gr_framebuffer[0].height; + vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8; + if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { + perror("active fb swap failed"); + } + displayed_buffer = n; +} + +static int get_framebuffer() { int fd; void *bits; + struct fb_fix_screeninfo fi; + fd = open("/dev/graphics/fb0", O_RDWR); if (fd < 0) { perror("cannot open fb0"); return -1; } - if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { + if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { perror("failed to get fb0 info"); close(fd); return -1; } - vi.bits_per_pixel = PIXEL_SIZE * 8; - if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_BGRA_8888) { - vi.red.offset = 8; - vi.red.length = 8; - vi.green.offset = 16; - vi.green.length = 8; - vi.blue.offset = 24; - vi.blue.length = 8; - vi.transp.offset = 0; - vi.transp.length = 8; - } else if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_RGBX_8888) { - vi.red.offset = 24; - vi.red.length = 8; - vi.green.offset = 16; - vi.green.length = 8; - vi.blue.offset = 8; - vi.blue.length = 8; - vi.transp.offset = 0; - vi.transp.length = 8; - } else { /* RGB565*/ - vi.red.offset = 11; - vi.red.length = 5; - vi.green.offset = 5; - vi.green.length = 6; - vi.blue.offset = 0; - vi.blue.length = 5; - vi.transp.offset = 0; - vi.transp.length = 0; - } - if (ioctl(fd, FBIOPUT_VSCREENINFO, &vi) < 0) { - perror("failed to put fb0 info"); - close(fd); - return -1; - } - - if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { + if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { perror("failed to get fb0 info"); close(fd); return -1; } + // We print this out for informational purposes only, but + // throughout we assume that the framebuffer device uses an RGBX + // pixel format. This is the case for every development device I + // have access to. For some of those devices (eg, hammerhead aka + // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a + // different format (XBGR) but actually produces the correct + // results on the display when you write RGBX. + // + // If you have a device that actually *needs* another pixel format + // (ie, BGRX, or 565), patches welcome... + + printf("fb0 reports (possibly inaccurate):\n" + " vi.bits_per_pixel = %d\n" + " vi.red.offset = %3d .length = %3d\n" + " vi.green.offset = %3d .length = %3d\n" + " vi.blue.offset = %3d .length = %3d\n", + vi.bits_per_pixel, + vi.red.offset, vi.red.length, + vi.green.offset, vi.green.length, + vi.blue.offset, vi.blue.length); + bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (bits == MAP_FAILED) { perror("failed to mmap framebuffer"); @@ -137,79 +134,59 @@ static int get_framebuffer(GGLSurface *fb) overscan_offset_x = vi.xres * overscan_percent / 100; overscan_offset_y = vi.yres * overscan_percent / 100; - fb->version = sizeof(*fb); - fb->width = vi.xres; - fb->height = vi.yres; - fb->stride = fi.line_length/PIXEL_SIZE; - fb->data = bits; - fb->format = PIXEL_FORMAT; - memset(fb->data, 0, vi.yres * fi.line_length); - - fb++; + gr_framebuffer[0].width = vi.xres; + gr_framebuffer[0].height = vi.yres; + gr_framebuffer[0].row_bytes = fi.line_length; + gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8; + gr_framebuffer[0].data = bits; + memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes); /* check if we can use double buffering */ - if (vi.yres * fi.line_length * 2 > fi.smem_len) - return fd; + if (vi.yres * fi.line_length * 2 <= fi.smem_len) { + double_buffered = true; - double_buffering = 1; + memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface)); + gr_framebuffer[1].data = gr_framebuffer[0].data + + gr_framebuffer[0].height * gr_framebuffer[0].row_bytes; - fb->version = sizeof(*fb); - fb->width = vi.xres; - fb->height = vi.yres; - fb->stride = fi.line_length/PIXEL_SIZE; - fb->data = (void*) (((char*) bits) + vi.yres * fi.line_length); - fb->format = PIXEL_FORMAT; - memset(fb->data, 0, vi.yres * fi.line_length); - - return fd; -} - -static void get_memory_surface(GGLSurface* ms) { - ms->version = sizeof(*ms); - ms->width = vi.xres; - ms->height = vi.yres; - ms->stride = fi.line_length/PIXEL_SIZE; - ms->data = malloc(fi.line_length * vi.yres); - ms->format = PIXEL_FORMAT; -} + gr_draw = gr_framebuffer+1; -static void set_active_framebuffer(unsigned n) -{ - if (n > 1 || !double_buffering) return; - vi.yres_virtual = vi.yres * NUM_BUFFERS; - vi.yoffset = n * vi.yres; - vi.bits_per_pixel = PIXEL_SIZE * 8; - if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { - perror("active fb swap failed"); + } else { + double_buffered = false; + + // Without double-buffering, we allocate RAM for a buffer to + // draw in, and then "flipping" the buffer consists of a + // memcpy from the buffer we allocated to the framebuffer. + + gr_draw = (GRSurface*) malloc(sizeof(GRSurface)); + memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface)); + gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes); + if (!gr_draw->data) { + perror("failed to allocate in-memory surface"); + return -1; + } } -} -void gr_flip(void) -{ - GGLContext *gl = gr_context; - - /* swap front and back buffers */ - if (double_buffering) - gr_active_fb = (gr_active_fb + 1) & 1; + memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes); + gr_fb_fd = fd; + set_displayed_framebuffer(0); - /* copy data from the in-memory surface to the buffer we're about - * to make active. */ - memcpy(gr_framebuffer[gr_active_fb].data, gr_mem_surface.data, - fi.line_length * vi.yres); - - /* inform the display driver */ - set_active_framebuffer(gr_active_fb); + return fd; } -void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +void gr_flip(void) { - GGLContext *gl = gr_context; - GGLint color[4]; - color[0] = ((r << 8) | r) + 1; - color[1] = ((g << 8) | g) + 1; - color[2] = ((b << 8) | b) + 1; - color[3] = ((a << 8) | a) + 1; - gl->color4xv(gl, color); + if (double_buffered) { + // Change gr_draw to point to the buffer currently displayed, + // then flip the driver so we're displaying the other buffer + // instead. + gr_draw = gr_framebuffer + displayed_buffer; + set_displayed_framebuffer(1-displayed_buffer); + } else { + // Copy from the in-memory surface to the framebuffer. + memcpy(gr_framebuffer[0].data, gr_draw->data, + gr_draw->height * gr_draw->row_bytes); + } } int gr_measure(const char *s) @@ -223,58 +200,118 @@ void gr_font_size(int *x, int *y) *y = gr_font->cheight; } -int gr_text(int x, int y, const char *s, int bold) +static void text_blend(unsigned char* src_p, int src_row_bytes, + unsigned char* dst_p, int dst_row_bytes, + int width, int height) +{ + int i, j; + for (j = 0; j < height; ++j) { + unsigned char* sx = src_p; + unsigned char* px = dst_p; + for (i = 0; i < width; ++i) { + unsigned char a = *sx++; + if (gr_current_a < 255) a = ((int)a * gr_current_a) / 255; + if (a == 255) { + *px++ = gr_current_r; + *px++ = gr_current_g; + *px++ = gr_current_b; + px++; + } else if (a > 0) { + *px = (*px * (255-a) + gr_current_r * a) / 255; + ++px; + *px = (*px * (255-a) + gr_current_g * a) / 255; + ++px; + *px = (*px * (255-a) + gr_current_b * a) / 255; + ++px; + ++px; + } else { + px += 4; + } + } + src_p += src_row_bytes; + dst_p += dst_row_bytes; + } +} + + +void gr_text(int x, int y, const char *s, int bold) { - GGLContext *gl = gr_context; GRFont *font = gr_font; unsigned off; - if (!font->texture) return x; + if (!font->texture) return; + if (gr_current_a == 0) return; bold = bold && (font->texture->height != font->cheight); x += overscan_offset_x; y += overscan_offset_y; - gl->bindTexture(gl, font->texture); - gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); - gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - gl->enable(gl, GGL_TEXTURE_2D); - while((off = *s++)) { off -= 32; + if (outside(x, y) || outside(x+font->cwidth-1, y+font->cheight-1)) break; if (off < 96) { - gl->texCoord2i(gl, (off * font->cwidth) - x, - (bold ? font->cheight : 0) - y); - gl->recti(gl, x, y, x + font->cwidth, y + font->cheight); + + unsigned char* src_p = font->texture->data + (off * font->cwidth) + + (bold ? font->cheight * font->texture->row_bytes : 0); + unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes; + + text_blend(src_p, font->texture->row_bytes, + dst_p, gr_draw->row_bytes, + font->cwidth, font->cheight); + } x += font->cwidth; } - - return x; } -void gr_texticon(int x, int y, gr_surface icon) { - if (gr_context == NULL || icon == NULL) { +void gr_texticon(int x, int y, GRSurface* icon) { + if (icon == NULL) return; + + if (icon->pixel_bytes != 1) { + printf("gr_texticon: source has wrong format\n"); return; } - GGLContext* gl = gr_context; x += overscan_offset_x; y += overscan_offset_y; - gl->bindTexture(gl, (GGLSurface*) icon); - gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); - gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - gl->enable(gl, GGL_TEXTURE_2D); + if (outside(x, y) || outside(x+icon->width-1, y+icon->height-1)) return; + + unsigned char* src_p = icon->data; + unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes; - int w = gr_get_width(icon); - int h = gr_get_height(icon); + text_blend(src_p, icon->row_bytes, + dst_p, gr_draw->row_bytes, + icon->width, icon->height); +} - gl->texCoord2i(gl, -x, -y); - gl->recti(gl, x, y, x+gr_get_width(icon), y+gr_get_height(icon)); +void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + gr_current_r = r; + gr_current_g = g; + gr_current_b = b; + gr_current_a = a; +} + +void gr_clear() +{ + if (gr_current_r == gr_current_g && + gr_current_r == gr_current_b) { + memset(gr_draw->data, gr_current_r, gr_draw->height * gr_draw->row_bytes); + } else { + int x, y; + unsigned char* px = gr_draw->data; + for (y = 0; y < gr_draw->height; ++y) { + for (x = 0; x < gr_draw->width; ++x) { + *px++ = gr_current_r; + *px++ = gr_current_g; + *px++ = gr_current_b; + px++; + } + px += gr_draw->row_bytes - (gr_draw->width * gr_draw->pixel_bytes); + } + } } void gr_fill(int x1, int y1, int x2, int y2) @@ -285,48 +322,82 @@ void gr_fill(int x1, int y1, int x2, int y2) x2 += overscan_offset_x; y2 += overscan_offset_y; - GGLContext *gl = gr_context; - gl->disable(gl, GGL_TEXTURE_2D); - gl->recti(gl, x1, y1, x2, y2); + if (outside(x1, y1) || outside(x2-1, y2-1)) return; + + unsigned char* p = gr_draw->data + y1 * gr_draw->row_bytes + x1 * gr_draw->pixel_bytes; + if (gr_current_a == 255) { + int x, y; + for (y = y1; y < y2; ++y) { + unsigned char* px = p; + for (x = x1; x < x2; ++x) { + *px++ = gr_current_r; + *px++ = gr_current_g; + *px++ = gr_current_b; + px++; + } + p += gr_draw->row_bytes; + } + } else if (gr_current_a > 0) { + int x, y; + for (y = y1; y < y2; ++y) { + unsigned char* px = p; + for (x = x1; x < x2; ++x) { + *px = (*px * (255-gr_current_a) + gr_current_r * gr_current_a) / 255; + ++px; + *px = (*px * (255-gr_current_a) + gr_current_g * gr_current_a) / 255; + ++px; + *px = (*px * (255-gr_current_a) + gr_current_b * gr_current_a) / 255; + ++px; + ++px; + } + p += gr_draw->row_bytes; + } + } } -void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) { - if (gr_context == NULL || source == NULL) { +void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) { + if (source == NULL) return; + + if (gr_draw->pixel_bytes != source->pixel_bytes) { + printf("gr_blit: source has wrong format\n"); return; } - GGLContext *gl = gr_context; dx += overscan_offset_x; dy += overscan_offset_y; - gl->bindTexture(gl, (GGLSurface*) source); - gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); - gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - gl->enable(gl, GGL_TEXTURE_2D); - gl->texCoord2i(gl, sx - dx, sy - dy); - gl->recti(gl, dx, dy, dx + w, dy + h); + if (outside(dx, dy) || outside(dx+w-1, dy+h-1)) return; + + unsigned char* src_p = source->data + sy*source->row_bytes + sx*source->pixel_bytes; + unsigned char* dst_p = gr_draw->data + dy*gr_draw->row_bytes + dx*gr_draw->pixel_bytes; + + int i; + for (i = 0; i < h; ++i) { + memcpy(dst_p, src_p, w * source->pixel_bytes); + src_p += source->row_bytes; + dst_p += gr_draw->row_bytes; + } } -unsigned int gr_get_width(gr_surface surface) { +unsigned int gr_get_width(GRSurface* surface) { if (surface == NULL) { return 0; } - return ((GGLSurface*) surface)->width; + return surface->width; } -unsigned int gr_get_height(gr_surface surface) { +unsigned int gr_get_height(GRSurface* surface) { if (surface == NULL) { return 0; } - return ((GGLSurface*) surface)->height; + return surface->height; } static void gr_init_font(void) { gr_font = calloc(sizeof(*gr_font), 1); - int res = res_create_surface("font", (void**)&(gr_font->texture)); + int res = res_create_surface("font", &(gr_font->texture)); if (res == 0) { // The font image should be a 96x2 array of character images. The // columns are the printable ASCII characters 0x20 - 0x7f. The @@ -340,7 +411,8 @@ static void gr_init_font(void) gr_font->texture = malloc(sizeof(*gr_font->texture)); gr_font->texture->width = font.width; gr_font->texture->height = font.height; - gr_font->texture->stride = font.width; + gr_font->texture->row_bytes = font.width; + gr_font->texture->pixel_bytes = 1; unsigned char* bits = malloc(font.width * font.height); gr_font->texture->data = (void*) bits; @@ -355,17 +427,61 @@ static void gr_init_font(void) gr_font->cwidth = font.cwidth; gr_font->cheight = font.cheight; } +} - // interpret the grayscale as alpha - gr_font->texture->format = GGL_PIXEL_FORMAT_A_8; +#if 0 +// Exercises many of the gr_*() functions; useful for testing. +static void gr_test() { + GRSurface** images; + int frames; + int result = res_create_multi_surface("icon_installing", &frames, &images); + if (result < 0) { + printf("create surface %d\n", result); + gr_exit(); + return; + } + + time_t start = time(NULL); + int x; + for (x = 0; x <= 1200; ++x) { + if (x < 400) { + gr_color(0, 0, 0, 255); + } else { + gr_color(0, (x-400)%128, 0, 255); + } + gr_clear(); + + gr_color(255, 0, 0, 255); + gr_surface frame = images[x%frames]; + gr_blit(frame, 0, 0, frame->width, frame->height, x, 0); + + gr_color(255, 0, 0, 128); + gr_fill(400, 150, 600, 350); + + gr_color(255, 255, 255, 255); + gr_text(500, 225, "hello, world!", 0); + gr_color(255, 255, 0, 128); + gr_text(300+x, 275, "pack my box with five dozen liquor jugs", 1); + + gr_color(0, 0, 255, 128); + gr_fill(gr_draw->width - 200 - x, 300, gr_draw->width - x, 500); + + gr_flip(); + } + printf("getting end time\n"); + time_t end = time(NULL); + printf("got end time\n"); + printf("start %ld end %ld\n", (long)start, (long)end); + if (end > start) { + printf("%.2f fps\n", ((double)x) / (end-start)); + } } +#endif int gr_init(void) { - gglInit(&gr_context); - GGLContext *gl = gr_context; - gr_init_font(); + gr_vt_fd = open("/dev/tty0", O_RDWR | O_SYNC); if (gr_vt_fd < 0) { // This is non-fatal; post-Cupcake kernels don't have tty0. @@ -377,25 +493,16 @@ int gr_init(void) return -1; } - gr_fb_fd = get_framebuffer(gr_framebuffer); - if (gr_fb_fd < 0) { + if (get_framebuffer(&gr_framebuffer) < 0) { gr_exit(); return -1; } - get_memory_surface(&gr_mem_surface); - printf("framebuffer: fd %d (%d x %d)\n", - gr_fb_fd, gr_framebuffer[0].width, gr_framebuffer[0].height); - - /* start with 0 as front (displayed) and 1 as back (drawing) */ - gr_active_fb = 0; - set_active_framebuffer(0); - gl->colorBuffer(gl, &gr_mem_surface); + gr_fb_fd, gr_draw->width, gr_draw->height); - gl->activeTexture(gl, 0); - gl->enable(gl, GGL_BLEND); - gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA); + gr_flip(); + gr_flip(); gr_fb_blank(true); gr_fb_blank(false); @@ -408,26 +515,24 @@ void gr_exit(void) close(gr_fb_fd); gr_fb_fd = -1; - free(gr_mem_surface.data); - ioctl(gr_vt_fd, KDSETMODE, (void*) KD_TEXT); close(gr_vt_fd); gr_vt_fd = -1; + + if (!double_buffered) { + if (gr_draw) free(gr_draw->data); + free(gr_draw); + } } int gr_fb_width(void) { - return gr_framebuffer[0].width - 2*overscan_offset_x; + return gr_draw->width - 2*overscan_offset_x; } int gr_fb_height(void) { - return gr_framebuffer[0].height - 2*overscan_offset_y; -} - -gr_pixel *gr_fb_data(void) -{ - return (unsigned short *) gr_mem_surface.data; + return gr_draw->height - 2*overscan_offset_y; } void gr_fb_blank(bool blank) diff --git a/minui/minui.h b/minui/minui.h index 5c0defc40..cb930cc4f 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -23,22 +23,30 @@ extern "C" { #endif -typedef void* gr_surface; -typedef unsigned short gr_pixel; +typedef struct { + int width; + int height; + int row_bytes; + int pixel_bytes; + unsigned char* data; +} GRSurface; + +typedef GRSurface* gr_surface; int gr_init(void); void gr_exit(void); int gr_fb_width(void); int gr_fb_height(void); -gr_pixel *gr_fb_data(void); + void gr_flip(void); void gr_fb_blank(bool blank); +void gr_clear(); // clear entire surface to current color void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a); void gr_fill(int x1, int y1, int x2, int y2); -int gr_text(int x, int y, const char *s, int bold); - void gr_texticon(int x, int y, gr_surface icon); +void gr_text(int x, int y, const char *s, int bold); +void gr_texticon(int x, int y, gr_surface icon); int gr_measure(const char *s); void gr_font_size(int *x, int *y); diff --git a/minui/resources.c b/minui/resources.c index 5ad9b1d05..df813cb1e 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -27,25 +27,26 @@ #include #include -#include - #include #include "minui.h" extern char* locale; -// 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 * y; +#define SURFACE_DATA_ALIGNMENT 8 + +static gr_surface malloc_surface(size_t data_size) { + unsigned char* temp = malloc(sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT); + if (temp == NULL) return NULL; + gr_surface surface = (gr_surface) temp; + surface->data = temp + sizeof(GRSurface) + + (SURFACE_DATA_ALIGNMENT - (sizeof(GRSurface) % SURFACE_DATA_ALIGNMENT)); + return surface; } int res_create_surface(const char* name, gr_surface* pSurface) { char resPath[256]; - GGLSurface* surface = NULL; + gr_surface surface = NULL; int result = 0; unsigned char header[8]; png_structp png_ptr = NULL; @@ -100,9 +101,8 @@ int res_create_surface(const char* name, gr_surface* pSurface) { int channels = png_get_channels(png_ptr, info_ptr); - if (!(bit_depth == 8 && + if (!(bit_depth <= 8 && ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) || - (channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) || (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY))))) { return -7; @@ -110,30 +110,20 @@ int res_create_surface(const char* name, gr_surface* pSurface) { } size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width; - size_t pixelSize = stride * height; - surface = malloc(sizeof(GGLSurface) + pixelSize); + surface = malloc_surface(stride * height); if (surface == NULL) { result = -8; goto exit; } - unsigned char* pData = (unsigned char*) (surface + 1); - surface->version = sizeof(GGLSurface); + unsigned char* pData = surface->data; surface->width = width; surface->height = height; - surface->stride = width; /* Yes, pixels, not bytes */ - surface->data = pData; - surface->format = (channels == 3) ? GGL_PIXEL_FORMAT_RGBX_8888 : - ((color_type == PNG_COLOR_TYPE_PALETTE ? GGL_PIXEL_FORMAT_RGBA_8888 : GGL_PIXEL_FORMAT_L_8)); + surface->row_bytes = stride; + surface->pixel_bytes = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4); - int alpha = 0; - if (color_type == PNG_COLOR_TYPE_PALETTE) { - png_set_palette_to_rgb(png_ptr); - } - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { - png_set_tRNS_to_alpha(png_ptr); - alpha = 1; - } + int alpha = (channels == 4); + png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY) { alpha = 1; } @@ -188,7 +178,7 @@ int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurfac png_structp png_ptr = NULL; png_infop info_ptr = NULL; int i; - GGLSurface** surface = NULL; + gr_surface* surface = NULL; *pSurface = NULL; *frames = -1; @@ -240,9 +230,8 @@ int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurfac int channels = png_get_channels(png_ptr, info_ptr); - if (!(bit_depth == 8 && + if (!(bit_depth <= 8 && ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) || - (channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) || (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY))))) { return -7; @@ -271,38 +260,21 @@ int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurfac size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width; size_t pixelSize = stride * height / *frames; - surface = malloc(*frames * sizeof(GGLSurface*)); + surface = malloc(*frames * sizeof(gr_surface)); if (surface == NULL) { result = -8; goto exit; } for (i = 0; i < *frames; ++i) { - surface[i] = malloc(sizeof(GGLSurface) + pixelSize); - surface[i]->version = sizeof(GGLSurface); + surface[i] = malloc_surface(pixelSize); surface[i]->width = width; surface[i]->height = height / *frames; - surface[i]->stride = width; /* Yes, pixels, not bytes */ - surface[i]->data = (unsigned char*) (surface[i] + 1); - - if (channels == 3) { - surface[i]->format = GGL_PIXEL_FORMAT_RGBX_8888; - } else if (color_type == PNG_COLOR_TYPE_PALETTE) { - surface[i]->format = GGL_PIXEL_FORMAT_RGBA_8888; - } else if (channels == 1) { - surface[i]->format = GGL_PIXEL_FORMAT_L_8; - } else { - surface[i]->format = GGL_PIXEL_FORMAT_RGBA_8888; - } + surface[i]->row_bytes = stride; + surface[i]->pixel_bytes = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4); } int alpha = (channels == 4); - if (color_type == PNG_COLOR_TYPE_PALETTE) { - png_set_palette_to_rgb(png_ptr); - } - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { - png_set_tRNS_to_alpha(png_ptr); - alpha = 1; - } + png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY) { alpha = 1; } @@ -376,7 +348,7 @@ static int matches_locale(const char* loc) { int res_create_localized_surface(const char* name, gr_surface* pSurface) { char resPath[256]; - GGLSurface* surface = NULL; + gr_surface surface = NULL; int result = 0; unsigned char header[8]; png_structp png_ptr = NULL; @@ -430,12 +402,14 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) { &color_type, NULL, NULL, NULL); int channels = png_get_channels(png_ptr, info_ptr); - if (!(bit_depth == 8 && + if (!(bit_depth <= 8 && (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) { return -7; goto exit; } + png_set_expand(png_ptr); + unsigned char* row = malloc(width); png_uint_32 y; for (y = 0; y < height; ++y) { @@ -448,19 +422,17 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) { if (y+1+h >= height || matches_locale(loc)) { printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y); - surface = malloc(sizeof(GGLSurface)); + surface = malloc_surface(w*h); if (surface == NULL) { result = -8; goto exit; } - unsigned char* pData = malloc(w*h); + unsigned char* pData = surface->data; - surface->version = sizeof(GGLSurface); surface->width = w; surface->height = h; - surface->stride = w; /* Yes, pixels, not bytes */ - surface->data = pData; - surface->format = GGL_PIXEL_FORMAT_A_8; + surface->row_bytes = w; + surface->pixel_bytes = 1; int i; for (i = 0; i < h; ++i, ++y) { @@ -493,8 +465,5 @@ exit: } void res_free_surface(gr_surface surface) { - GGLSurface* pSurface = (GGLSurface*) surface; - if (pSurface) { - free(pSurface); - } + free(surface); } diff --git a/mtdutils/flash_image.c b/mtdutils/flash_image.c index a39d60001..5657dfc82 100644 --- a/mtdutils/flash_image.c +++ b/mtdutils/flash_image.c @@ -24,6 +24,9 @@ #include "cutils/log.h" #include "mtdutils.h" +#ifdef LOG_TAG +#undef LOG_TAG +#endif #define LOG_TAG "flash_image" #define HEADER_SIZE 2048 // size of header to compare for equality diff --git a/screen_ui.cpp b/screen_ui.cpp index 38b21f84c..589c935dc 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -84,7 +84,7 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) { pagesIdentical = false; gr_color(0, 0, 0, 255); - gr_fill(0, 0, gr_fb_width(), gr_fb_height()); + gr_clear(); if (icon) { gr_surface surface = backgroundIcon[icon]; @@ -187,12 +187,12 @@ void ScreenRecoveryUI::SetColor(UIElement e) { // Should only be called with updateMutex locked. void ScreenRecoveryUI::draw_screen_locked() { - draw_background_locked(currentIcon); - draw_progress_locked(); - - if (show_text) { - SetColor(TEXT_FILL); - gr_fill(0, 0, gr_fb_width(), gr_fb_height()); + if (!show_text) { + draw_background_locked(currentIcon); + draw_progress_locked(); + } else { + gr_color(0, 0, 0, 255); + gr_clear(); int y = 0; int i = 0; -- cgit v1.2.3 From 5290f2087a314506e2926edd9640cf1feb793866 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 11 Mar 2014 13:22:04 -0700 Subject: separate fbdev-specific code out from minui Isolate the code that interacts with fbdev, in preparation for adding a new backend. Change-Id: I19105e9da1ca6408cebc110f7e2bb5abfb481ee9 --- minui/Android.mk | 2 +- minui/graphics.c | 166 ++++------------------------------------ minui/graphics.h | 49 ++++++++++++ minui/graphics_fbdev.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++ minui/minui.h | 2 + 5 files changed, 268 insertions(+), 152 deletions(-) create mode 100644 minui/graphics.h create mode 100644 minui/graphics_fbdev.c diff --git a/minui/Android.mk b/minui/Android.mk index 43e0ad33b..5cc84ac7a 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -1,7 +1,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := graphics.c events.c resources.c +LOCAL_SRC_FILES := graphics.c graphics_fbdev.c events.c resources.c LOCAL_C_INCLUDES +=\ external/libpng\ diff --git a/minui/graphics.c b/minui/graphics.c index c2fc9ea9e..f8ffa8941 100644 --- a/minui/graphics.c +++ b/minui/graphics.c @@ -32,6 +32,7 @@ #include "font_10x18.h" #include "minui.h" +#include "graphics.h" typedef struct { GRSurface* texture; @@ -40,155 +41,26 @@ typedef struct { } GRFont; static GRFont* gr_font = NULL; +static minui_backend* gr_backend = NULL; static int overscan_percent = OVERSCAN_PERCENT; static int overscan_offset_x = 0; static int overscan_offset_y = 0; -static int gr_fb_fd = -1; static int gr_vt_fd = -1; -static GRSurface gr_framebuffer[2]; -static bool double_buffered; -static GRSurface* gr_draw = NULL; -static int displayed_buffer; - static unsigned char gr_current_r = 255; static unsigned char gr_current_g = 255; static unsigned char gr_current_b = 255; static unsigned char gr_current_a = 255; -static struct fb_var_screeninfo vi; +static GRSurface* gr_draw = NULL; static bool outside(int x, int y) { return x < 0 || x >= gr_draw->width || y < 0 || y >= gr_draw->height; } -static void set_displayed_framebuffer(unsigned n) -{ - if (n > 1 || !double_buffered) return; - - vi.yres_virtual = gr_framebuffer[0].height * 2; - vi.yoffset = n * gr_framebuffer[0].height; - vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8; - if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { - perror("active fb swap failed"); - } - displayed_buffer = n; -} - -static int get_framebuffer() -{ - int fd; - void *bits; - - struct fb_fix_screeninfo fi; - - fd = open("/dev/graphics/fb0", O_RDWR); - if (fd < 0) { - perror("cannot open fb0"); - return -1; - } - - if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { - perror("failed to get fb0 info"); - close(fd); - return -1; - } - - if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { - perror("failed to get fb0 info"); - close(fd); - return -1; - } - - // We print this out for informational purposes only, but - // throughout we assume that the framebuffer device uses an RGBX - // pixel format. This is the case for every development device I - // have access to. For some of those devices (eg, hammerhead aka - // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a - // different format (XBGR) but actually produces the correct - // results on the display when you write RGBX. - // - // If you have a device that actually *needs* another pixel format - // (ie, BGRX, or 565), patches welcome... - - printf("fb0 reports (possibly inaccurate):\n" - " vi.bits_per_pixel = %d\n" - " vi.red.offset = %3d .length = %3d\n" - " vi.green.offset = %3d .length = %3d\n" - " vi.blue.offset = %3d .length = %3d\n", - vi.bits_per_pixel, - vi.red.offset, vi.red.length, - vi.green.offset, vi.green.length, - vi.blue.offset, vi.blue.length); - - bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (bits == MAP_FAILED) { - perror("failed to mmap framebuffer"); - close(fd); - return -1; - } - - overscan_offset_x = vi.xres * overscan_percent / 100; - overscan_offset_y = vi.yres * overscan_percent / 100; - - gr_framebuffer[0].width = vi.xres; - gr_framebuffer[0].height = vi.yres; - gr_framebuffer[0].row_bytes = fi.line_length; - gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8; - gr_framebuffer[0].data = bits; - memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes); - - /* check if we can use double buffering */ - if (vi.yres * fi.line_length * 2 <= fi.smem_len) { - double_buffered = true; - - memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface)); - gr_framebuffer[1].data = gr_framebuffer[0].data + - gr_framebuffer[0].height * gr_framebuffer[0].row_bytes; - - gr_draw = gr_framebuffer+1; - - } else { - double_buffered = false; - - // Without double-buffering, we allocate RAM for a buffer to - // draw in, and then "flipping" the buffer consists of a - // memcpy from the buffer we allocated to the framebuffer. - - gr_draw = (GRSurface*) malloc(sizeof(GRSurface)); - memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface)); - gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes); - if (!gr_draw->data) { - perror("failed to allocate in-memory surface"); - return -1; - } - } - - memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes); - gr_fb_fd = fd; - set_displayed_framebuffer(0); - - return fd; -} - -void gr_flip(void) -{ - if (double_buffered) { - // Change gr_draw to point to the buffer currently displayed, - // then flip the driver so we're displaying the other buffer - // instead. - gr_draw = gr_framebuffer + displayed_buffer; - set_displayed_framebuffer(1-displayed_buffer); - } else { - // Copy from the in-memory surface to the framebuffer. - memcpy(gr_framebuffer[0].data, gr_draw->data, - gr_draw->height * gr_draw->row_bytes); - } -} - int gr_measure(const char *s) { return gr_font->cwidth * strlen(s); @@ -466,7 +338,7 @@ static void gr_test() { gr_color(0, 0, 255, 128); gr_fill(gr_draw->width - 200 - x, 300, gr_draw->width - x, 500); - gr_flip(); + gr_draw = gr_backend->flip(gr_backend); } printf("getting end time\n"); time_t end = time(NULL); @@ -478,6 +350,10 @@ static void gr_test() { } #endif +void gr_flip() { + gr_draw = gr_backend->flip(gr_backend); +} + int gr_init(void) { gr_init_font(); @@ -493,36 +369,28 @@ int gr_init(void) return -1; } - if (get_framebuffer(&gr_framebuffer) < 0) { - gr_exit(); + gr_backend = open_fbdev(); + gr_draw = gr_backend->init(gr_backend); + if (gr_draw == NULL) { return -1; } - printf("framebuffer: fd %d (%d x %d)\n", - gr_fb_fd, gr_draw->width, gr_draw->height); + overscan_offset_x = gr_draw->width * overscan_percent / 100; + overscan_offset_y = gr_draw->height * overscan_percent / 100; gr_flip(); gr_flip(); - gr_fb_blank(true); - gr_fb_blank(false); - return 0; } void gr_exit(void) { - close(gr_fb_fd); - gr_fb_fd = -1; + gr_backend->exit(gr_backend); ioctl(gr_vt_fd, KDSETMODE, (void*) KD_TEXT); close(gr_vt_fd); gr_vt_fd = -1; - - if (!double_buffered) { - if (gr_draw) free(gr_draw->data); - free(gr_draw); - } } int gr_fb_width(void) @@ -537,9 +405,5 @@ int gr_fb_height(void) void gr_fb_blank(bool blank) { - int ret; - - ret = ioctl(gr_fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK); - if (ret < 0) - perror("ioctl(): blank"); + gr_backend->blank(gr_backend, blank); } diff --git a/minui/graphics.h b/minui/graphics.h new file mode 100644 index 000000000..94b89a554 --- /dev/null +++ b/minui/graphics.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 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 _GRAPHICS_H_ +#define _GRAPHICS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "minui.h" + +typedef struct minui_backend { + // Initializes the backend and returns a gr_surface to draw into. + gr_surface (*init)(struct minui_backend*); + + // Causes the current drawing surface (returned by the most recent + // call to flip() or init()) to be displayed, and returns a new + // drawing surface. + gr_surface (*flip)(struct minui_backend*); + + // Blank (or unblank) the screen. + void (*blank)(struct minui_backend*, bool); + + // Device cleanup when drawing is done. + void (*exit)(struct minui_backend*); +} minui_backend; + +minui_backend* open_fbdev(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/minui/graphics_fbdev.c b/minui/graphics_fbdev.c new file mode 100644 index 000000000..6a6330b22 --- /dev/null +++ b/minui/graphics_fbdev.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2014 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 +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include "minui.h" +#include "graphics.h" + +static gr_surface fbdev_init(minui_backend*); +static gr_surface fbdev_flip(minui_backend*); +static void fbdev_blank(minui_backend*, bool); +static void fbdev_exit(minui_backend*); + +static GRSurface gr_framebuffer[2]; +static bool double_buffered; +static GRSurface* gr_draw = NULL; +static int displayed_buffer; + +static struct fb_var_screeninfo vi; +static int fb_fd = -1; + +static minui_backend my_backend = { + .init = fbdev_init, + .flip = fbdev_flip, + .blank = fbdev_blank, + .exit = fbdev_exit, +}; + +minui_backend* open_fbdev() { + return &my_backend; +} + +static void fbdev_blank(minui_backend* backend, bool blank) +{ + int ret; + + ret = ioctl(fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK); + if (ret < 0) + perror("ioctl(): blank"); +} + +static void set_displayed_framebuffer(unsigned n) +{ + if (n > 1 || !double_buffered) return; + + vi.yres_virtual = gr_framebuffer[0].height * 2; + vi.yoffset = n * gr_framebuffer[0].height; + vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8; + if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { + perror("active fb swap failed"); + } + displayed_buffer = n; +} + +static gr_surface fbdev_init(minui_backend* backend) { + int fd; + void *bits; + + struct fb_fix_screeninfo fi; + + fd = open("/dev/graphics/fb0", O_RDWR); + if (fd < 0) { + perror("cannot open fb0"); + return NULL; + } + + if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) { + perror("failed to get fb0 info"); + close(fd); + return NULL; + } + + if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) { + perror("failed to get fb0 info"); + close(fd); + return NULL; + } + + // We print this out for informational purposes only, but + // throughout we assume that the framebuffer device uses an RGBX + // pixel format. This is the case for every development device I + // have access to. For some of those devices (eg, hammerhead aka + // Nexus 5), FBIOGET_VSCREENINFO *reports* that it wants a + // different format (XBGR) but actually produces the correct + // results on the display when you write RGBX. + // + // If you have a device that actually *needs* another pixel format + // (ie, BGRX, or 565), patches welcome... + + printf("fb0 reports (possibly inaccurate):\n" + " vi.bits_per_pixel = %d\n" + " vi.red.offset = %3d .length = %3d\n" + " vi.green.offset = %3d .length = %3d\n" + " vi.blue.offset = %3d .length = %3d\n", + vi.bits_per_pixel, + vi.red.offset, vi.red.length, + vi.green.offset, vi.green.length, + vi.blue.offset, vi.blue.length); + + bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (bits == MAP_FAILED) { + perror("failed to mmap framebuffer"); + close(fd); + return NULL; + } + + gr_framebuffer[0].width = vi.xres; + gr_framebuffer[0].height = vi.yres; + gr_framebuffer[0].row_bytes = fi.line_length; + gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8; + gr_framebuffer[0].data = bits; + memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes); + + /* check if we can use double buffering */ + if (vi.yres * fi.line_length * 2 <= fi.smem_len) { + double_buffered = true; + + memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(GRSurface)); + gr_framebuffer[1].data = gr_framebuffer[0].data + + gr_framebuffer[0].height * gr_framebuffer[0].row_bytes; + + gr_draw = gr_framebuffer+1; + + } else { + double_buffered = false; + + // Without double-buffering, we allocate RAM for a buffer to + // draw in, and then "flipping" the buffer consists of a + // memcpy from the buffer we allocated to the framebuffer. + + gr_draw = (GRSurface*) malloc(sizeof(GRSurface)); + memcpy(gr_draw, gr_framebuffer, sizeof(GRSurface)); + gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes); + if (!gr_draw->data) { + perror("failed to allocate in-memory surface"); + return NULL; + } + } + + memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes); + fb_fd = fd; + set_displayed_framebuffer(0); + + printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height); + + fbdev_blank(backend, true); + fbdev_blank(backend, false); + + return gr_draw; +} + +static gr_surface fbdev_flip(minui_backend* backend) { + if (double_buffered) { + // Change gr_draw to point to the buffer currently displayed, + // then flip the driver so we're displaying the other buffer + // instead. + gr_draw = gr_framebuffer + displayed_buffer; + set_displayed_framebuffer(1-displayed_buffer); + } else { + // Copy from the in-memory surface to the framebuffer. + memcpy(gr_framebuffer[0].data, gr_draw->data, + gr_draw->height * gr_draw->row_bytes); + } + return gr_draw; +} + +static void fbdev_exit(minui_backend* backend) { + close(fb_fd); + fb_fd = -1; + + if (!double_buffered && gr_draw) { + free(gr_draw->data); + free(gr_draw); + } + gr_draw = NULL; +} diff --git a/minui/minui.h b/minui/minui.h index cb930cc4f..328da1e6c 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -17,6 +17,8 @@ #ifndef _MINUI_H_ #define _MINUI_H_ +#include + #include #ifdef __cplusplus -- cgit v1.2.3 From f3bb31c32fa879ccce358c15c93b7bd8582d1756 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Fri, 14 Mar 2014 09:39:48 -0700 Subject: Recovery 64-bit compile issues Change-Id: I92d5abd1a628feab3b0246924fab7f97ba3b9d34 --- applypatch/applypatch.c | 12 ++++++------ applypatch/imgpatch.c | 7 ++++--- minadbd/sockets.c | 3 ++- minadbd/transport.c | 4 ++-- minadbd/usb_linux_client.c | 2 +- minui/graphics_fbdev.c | 7 ++++--- minui/resources.c | 2 +- recovery.cpp | 4 ++-- tools/ota/add-property-tag.c | 4 ++-- updater/install.c | 4 ++-- verifier.cpp | 14 +++++++------- 11 files changed, 33 insertions(+), 30 deletions(-) diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 6b8da2a8a..9d32ee904 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -247,7 +247,7 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { break; } if (next != read) { - printf("short read (%d bytes of %d) for partition \"%s\"\n", + printf("short read (%zu bytes of %zu) for partition \"%s\"\n", read, next, partition); free(file->data); file->data = NULL; @@ -274,7 +274,7 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_SIZE) == 0) { // we have a match. stop reading the partition; we'll return // the data we've read so far. - printf("partition read matched size %d sha %s\n", + printf("partition read matched size %zu sha %s\n", size[index[i]], sha1sum[index[i]]); break; } @@ -402,7 +402,7 @@ int WriteToPartition(unsigned char* data, size_t len, size_t written = mtd_write_data(ctx, (char*)data, len); if (written != len) { - printf("only wrote %d of %d bytes to MTD %s\n", + printf("only wrote %zu of %zu bytes to MTD %s\n", written, len, partition); mtd_write_close(ctx); return -1; @@ -482,20 +482,20 @@ int WriteToPartition(unsigned char* data, size_t len, if (errno == EINTR) { read_count = 0; } else { - printf("verify read error %s at %d: %s\n", + printf("verify read error %s at %zu: %s\n", partition, p, strerror(errno)); return -1; } } if ((size_t)read_count < to_read) { - printf("short verify read %s at %d: %d %d %s\n", + printf("short verify read %s at %zu: %zd %zu %s\n", partition, p, read_count, to_read, strerror(errno)); } so_far += read_count; } if (memcmp(buffer, data+p, to_read)) { - printf("verification failed starting at %d\n", p); + printf("verification failed starting at %zu\n", p); start = p; break; } diff --git a/applypatch/imgpatch.c b/applypatch/imgpatch.c index 3a1df3872..af4d07281 100644 --- a/applypatch/imgpatch.c +++ b/applypatch/imgpatch.c @@ -18,6 +18,7 @@ // format. #include +#include #include #include #include @@ -35,7 +36,7 @@ * file, and update the SHA context with the output data as well. * Return 0 on success. */ -int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, +int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, const Value* patch, SinkFn sink, void* token, SHA_CTX* ctx, const Value* bonus_data) { @@ -132,7 +133,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, unsigned char* expanded_source = malloc(expanded_len); if (expanded_source == NULL) { - printf("failed to allocate %d bytes for expanded_source\n", + printf("failed to allocate %zu bytes for expanded_source\n", expanded_len); return -1; } @@ -163,7 +164,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, // We should have filled the output buffer exactly, except // for the bonus_size. if (strm.avail_out != bonus_size) { - printf("source inflation short by %d bytes\n", strm.avail_out-bonus_size); + printf("source inflation short by %zu bytes\n", strm.avail_out-bonus_size); return -1; } inflateEnd(&strm); diff --git a/minadbd/sockets.c b/minadbd/sockets.c index 2dd646159..817410d13 100644 --- a/minadbd/sockets.c +++ b/minadbd/sockets.c @@ -319,7 +319,8 @@ static void local_socket_event_func(int fd, unsigned ev, void *_s) while(avail > 0) { r = adb_read(fd, x, avail); - D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%d\n", s->id, s->fd, r, r<0?errno:0, avail); + D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu\n", + s->id, s->fd, r, r<0?errno:0, avail); if(r > 0) { avail -= r; x += r; diff --git a/minadbd/transport.c b/minadbd/transport.c index 4c0c97f75..92679f518 100644 --- a/minadbd/transport.c +++ b/minadbd/transport.c @@ -713,7 +713,7 @@ int readx(int fd, void *ptr, size_t len) char *p = ptr; int r; #if ADB_TRACE - int len0 = len; + size_t len0 = len; #endif D("readx: fd=%d wanted=%d\n", fd, (int)len); while(len > 0) { @@ -734,7 +734,7 @@ int readx(int fd, void *ptr, size_t len) } #if ADB_TRACE - D("readx: fd=%d wanted=%d got=%d\n", fd, len0, len0 - len); + D("readx: fd=%d wanted=%zu got=%zu\n", fd, len0, len0 - len); dump_hex( ptr, len0 ); #endif return 0; diff --git a/minadbd/usb_linux_client.c b/minadbd/usb_linux_client.c index c135d6396..29bab1558 100644 --- a/minadbd/usb_linux_client.c +++ b/minadbd/usb_linux_client.c @@ -388,7 +388,7 @@ static int bulk_read(int bulk_out, char *buf, size_t length) ret = adb_read(bulk_out, buf + count, length - count); if (ret < 0) { if (errno != EINTR) { - D("[ bulk_read failed fd=%d length=%d count=%d ]\n", + D("[ bulk_read failed fd=%d length=%zu count=%zu ]\n", bulk_out, length, count); return ret; } diff --git a/minui/graphics_fbdev.c b/minui/graphics_fbdev.c index 6a6330b22..bb91975a9 100644 --- a/minui/graphics_fbdev.c +++ b/minui/graphics_fbdev.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -55,7 +56,7 @@ minui_backend* open_fbdev() { return &my_backend; } -static void fbdev_blank(minui_backend* backend, bool blank) +static void fbdev_blank(minui_backend* backend __unused, bool blank) { int ret; @@ -174,7 +175,7 @@ static gr_surface fbdev_init(minui_backend* backend) { return gr_draw; } -static gr_surface fbdev_flip(minui_backend* backend) { +static gr_surface fbdev_flip(minui_backend* backend __unused) { if (double_buffered) { // Change gr_draw to point to the buffer currently displayed, // then flip the driver so we're displaying the other buffer @@ -189,7 +190,7 @@ static gr_surface fbdev_flip(minui_backend* backend) { return gr_draw; } -static void fbdev_exit(minui_backend* backend) { +static void fbdev_exit(minui_backend* backend __unused) { close(fb_fd); fb_fd = -1; diff --git a/minui/resources.c b/minui/resources.c index df813cb1e..a6528b357 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -95,7 +95,7 @@ int res_create_surface(const char* name, gr_surface* pSurface) { png_read_info(png_ptr, info_ptr); int color_type, bit_depth; - size_t width, height; + png_uint_32 width, height; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); diff --git a/recovery.cpp b/recovery.cpp index d803cadf1..448f315b0 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -174,11 +174,11 @@ get_args(int *argc, char ***argv) { get_bootloader_message(&boot); // this may fail, leaving a zeroed structure if (boot.command[0] != 0 && boot.command[0] != 255) { - LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command); + LOGI("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command); } if (boot.status[0] != 0 && boot.status[0] != 255) { - LOGI("Boot status: %.*s\n", sizeof(boot.status), boot.status); + LOGI("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status); } // --- if arguments weren't supplied, look in the bootloader control block diff --git a/tools/ota/add-property-tag.c b/tools/ota/add-property-tag.c index 5277edd9c..aab30b2d0 100644 --- a/tools/ota/add-property-tag.c +++ b/tools/ota/add-property-tag.c @@ -57,9 +57,9 @@ void write_tagged(FILE *out, const char *line, const char *tag, int number) { const char *end = line + strlen(line); while (end > line && isspace(end[-1])) --end; if (number > 0) { - fprintf(out, "%.*s%s%d%s", end - line, line, tag, number, end); + fprintf(out, "%.*s%s%d%s", (int)(end - line), line, tag, number, end); } else { - fprintf(out, "%.*s%s%s", end - line, line, tag, end); + fprintf(out, "%.*s%s%s", (int)(end - line), line, tag, end); } } diff --git a/updater/install.c b/updater/install.c index 6a8a6b3fc..1455557ca 100644 --- a/updater/install.c +++ b/updater/install.c @@ -864,7 +864,7 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { buffer = malloc(st.st_size+1); if (buffer == NULL) { - ErrorAbort(state, "%s: failed to alloc %lld bytes", name, st.st_size+1); + ErrorAbort(state, "%s: failed to alloc %lld bytes", name, (long long)st.st_size+1); goto done; } @@ -877,7 +877,7 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { if (fread(buffer, 1, st.st_size, f) != st.st_size) { ErrorAbort(state, "%s: failed to read %lld bytes from %s", - name, st.st_size+1, filename); + name, (long long)st.st_size+1, filename); fclose(f); goto done; } diff --git a/verifier.cpp b/verifier.cpp index 0930fbd15..019552b92 100644 --- a/verifier.cpp +++ b/verifier.cpp @@ -152,7 +152,7 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys size_t comment_size = footer[4] + (footer[5] << 8); size_t signature_start = footer[0] + (footer[1] << 8); - LOGI("comment is %d bytes; signature %d bytes from end\n", + LOGI("comment is %zu bytes; signature %zu bytes from end\n", comment_size, signature_start); if (signature_start <= FOOTER_SIZE) { @@ -292,24 +292,24 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys if (pKeys[i].key_type == Certificate::RSA) { if (sig_der_length < RSANUMBYTES) { // "signature" block isn't big enough to contain an RSA block. - LOGI("signature is too short for RSA key %d\n", i); + LOGI("signature is too short for RSA key %zu\n", i); continue; } if (!RSA_verify(pKeys[i].rsa, sig_der, RSANUMBYTES, hash, pKeys[i].hash_len)) { - LOGI("failed to verify against RSA key %d\n", i); + LOGI("failed to verify against RSA key %zu\n", i); continue; } - LOGI("whole-file signature verified against RSA key %d\n", i); + LOGI("whole-file signature verified against RSA key %zu\n", i); free(sig_der); return VERIFY_SUCCESS; } else if (pKeys[i].key_type == Certificate::EC && pKeys[i].hash_len == SHA256_DIGEST_SIZE) { p256_int r, s; if (!dsa_sig_unpack(sig_der, sig_der_length, &r, &s)) { - LOGI("Not a DSA signature block for EC key %d\n", i); + LOGI("Not a DSA signature block for EC key %zu\n", i); continue; } @@ -317,11 +317,11 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys p256_from_bin(hash, &p256_hash); if (!p256_ecdsa_verify(&(pKeys[i].ec->x), &(pKeys[i].ec->y), &p256_hash, &r, &s)) { - LOGI("failed to verify against EC key %d\n", i); + LOGI("failed to verify against EC key %zu\n", i); continue; } - LOGI("whole-file signature verified against EC key %d\n", i); + LOGI("whole-file signature verified against EC key %zu\n", i); free(sig_der); return VERIFY_SUCCESS; } else { -- cgit v1.2.3 From a418aa7dd5e94cbf1ab2a6fa1c63f60e5e087d42 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 17 Mar 2014 12:10:02 -0700 Subject: refactor image resource loading code in minui Reduce the number of copies of libpng boilerplate. Rename res_create_* functions to be more clear. Make explicit the use of the framebuffer pixel format for images, and handle more combinations of input and output (eg, loading a grayscale image for display rather than use as a text alpha channel). Change-Id: I3d41c800a8f4c22b2f0167967ce6ee4d6b2b8846 --- minui/graphics.c | 2 +- minui/minui.h | 38 ++++- minui/resources.c | 440 +++++++++++++++++++++++++----------------------------- recovery.cpp | 2 +- screen_ui.cpp | 14 +- screen_ui.h | 1 + 6 files changed, 253 insertions(+), 244 deletions(-) diff --git a/minui/graphics.c b/minui/graphics.c index f8ffa8941..32b3361e3 100644 --- a/minui/graphics.c +++ b/minui/graphics.c @@ -269,7 +269,7 @@ static void gr_init_font(void) { gr_font = calloc(sizeof(*gr_font), 1); - int res = res_create_surface("font", &(gr_font->texture)); + int res = res_create_alpha_surface("font", &(gr_font->texture)); if (res == 0) { // The font image should be a 96x2 array of character images. The // columns are the printable ASCII characters 0x20 - 0x7f. The diff --git a/minui/minui.h b/minui/minui.h index 328da1e6c..d8d53fa27 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -80,10 +80,40 @@ void ev_dispatch(void); // Resources -// Returns 0 if no error, else negative. -int res_create_surface(const char* name, gr_surface* pSurface); -int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurface); -int res_create_localized_surface(const char* name, gr_surface* pSurface); +// res_create_*_surface() functions return 0 if no error, else +// negative. +// +// A "display" surface is one that is intended to be drawn to the +// screen with gr_blit(). An "alpha" surface is a grayscale image +// interpreted as an alpha mask used to render text in the current +// color (with gr_text() or gr_texticon()). +// +// All these functions load PNG images from "/res/images/${name}.png". + +// Load a single display surface from a PNG image. +int res_create_display_surface(const char* name, gr_surface* pSurface); + +// Load an array of display surfaces from a single PNG image. The PNG +// should have a 'Frames' text chunk whose value is the number of +// frames this image represents. The pixel data itself is interlaced +// by row. +int res_create_multi_display_surface(const char* name, + int* frames, gr_surface** pSurface); + +// Load a single alpha surface from a grayscale PNG image. +int res_create_alpha_surface(const char* name, gr_surface* pSurface); + +// Load part of a grayscale PNG image that is the first match for the +// given locale. The image is expected to be a composite of multiple +// translations of the same text, with special added rows that encode +// the subimages' size and intended locale in the pixel data. See +// development/tools/recovery_l10n for an app that will generate these +// specialized images from Android resources. +int res_create_localized_alpha_surface(const char* name, const char* locale, + gr_surface* pSurface); + +// Free a surface allocated by any of the res_create_*_surface() +// functions. void res_free_surface(gr_surface surface); #ifdef __cplusplus diff --git a/minui/resources.c b/minui/resources.c index a6528b357..69fd14bee 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -44,15 +44,11 @@ static gr_surface malloc_surface(size_t data_size) { return surface; } -int res_create_surface(const char* name, gr_surface* pSurface) { +static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr, + png_uint_32* width, png_uint_32* height, png_byte* channels) { char resPath[256]; - gr_surface surface = NULL; - int result = 0; unsigned char header[8]; - png_structp png_ptr = NULL; - png_infop info_ptr = NULL; - - *pSurface = NULL; + int result = 0; snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name); resPath[sizeof(resPath)-1] = '\0'; @@ -73,170 +69,182 @@ int res_create_surface(const char* name, gr_surface* pSurface) { goto exit; } - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) { + *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!*png_ptr) { result = -4; goto exit; } - info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { + *info_ptr = png_create_info_struct(*png_ptr); + if (!*info_ptr) { result = -5; goto exit; } - if (setjmp(png_jmpbuf(png_ptr))) { + if (setjmp(png_jmpbuf(*png_ptr))) { result = -6; goto exit; } - png_init_io(png_ptr, fp); - png_set_sig_bytes(png_ptr, sizeof(header)); - png_read_info(png_ptr, info_ptr); + png_init_io(*png_ptr, fp); + png_set_sig_bytes(*png_ptr, sizeof(header)); + png_read_info(*png_ptr, *info_ptr); int color_type, bit_depth; - png_uint_32 width, height; - png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, + png_get_IHDR(*png_ptr, *info_ptr, width, height, &bit_depth, &color_type, NULL, NULL, NULL); - int channels = png_get_channels(png_ptr, info_ptr); - - if (!(bit_depth <= 8 && - ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) || - (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE || - color_type == PNG_COLOR_TYPE_GRAY))))) { - return -7; + *channels = png_get_channels(*png_ptr, *info_ptr); + + if (bit_depth == 8 && *channels == 3 && color_type == PNG_COLOR_TYPE_RGB) { + // 8-bit RGB images: great, nothing to do. + } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_GRAY) { + // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray. + png_set_expand_gray_1_2_4_to_8(*png_ptr); + } else if (bit_depth == 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE) { + // paletted images: expand to 8-bit RGB. Note that we DON'T + // currently expand the tRNS chunk (if any) to an alpha + // channel, because minui doesn't support alpha channels in + // general. + png_set_palette_to_rgb(*png_ptr); + *channels = 3; + } else { + fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n", + bit_depth, (int) channels, color_type); + result = -7; goto exit; } - size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width; + return result; - surface = malloc_surface(stride * height); - if (surface == NULL) { - result = -8; - goto exit; + exit: + if (result < 0) { + png_destroy_read_struct(png_ptr, info_ptr, NULL); + } + if (fp != NULL) { + fclose(fp); } - unsigned char* pData = surface->data; + + return result; +} + +// "display" surfaces are transformed into the framebuffer's required +// pixel format (currently only RGBX is supported) at load time, so +// gr_blit() can be nothing more than a memcpy() for each row. The +// next two functions are the only ones that know anything about the +// framebuffer pixel format; they need to be modified if the +// framebuffer format changes (but nothing else should). + +// Allocate and return a gr_surface sufficient for storing an image of +// the indicated size in the framebuffer pixel format. +static gr_surface init_display_surface(png_uint_32 width, png_uint_32 height) { + gr_surface surface; + + surface = malloc_surface(width * height * 4); + if (surface == NULL) return NULL; + surface->width = width; surface->height = height; - surface->row_bytes = stride; - surface->pixel_bytes = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4); + surface->row_bytes = width * 4; + surface->pixel_bytes = 4; - int alpha = (channels == 4); - png_set_expand(png_ptr); - if (color_type == PNG_COLOR_TYPE_GRAY) { - alpha = 1; - } + return surface; +} - unsigned int y; - if (channels == 3 || (channels == 1 && !alpha)) { - 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 r = pRow[sx]; - unsigned char g = pRow[sx + 1]; - 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 + 3] = a; +// Copy 'input_row' to 'output_row', transforming it to the +// framebuffer pixel format. The input format depends on the value of +// 'channels': +// +// 1 - input is 8-bit grayscale +// 3 - input is 24-bit RGB +// 4 - input is 32-bit RGBA/RGBX +// +// 'width' is the number of pixels in the row. +static void transform_rgb_to_draw(unsigned char* input_row, + unsigned char* output_row, + int channels, int width) { + int x; + unsigned char* ip = input_row; + unsigned char* op = output_row; + + switch (channels) { + case 1: + // expand gray level to RGBX + for (x = 0; x < width; ++x) { + *op++ = *ip; + *op++ = *ip; + *op++ = *ip; + *op++ = 0xff; + ip++; } - } - } else { - for (y = 0; y < height; ++y) { - unsigned char* pRow = pData + y * stride; - png_read_row(png_ptr, pRow, NULL); - } - } - - *pSurface = (gr_surface) surface; + break; -exit: - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + case 3: + // expand RGBA to RGBX + for (x = 0; x < width; ++x) { + *op++ = *ip++; + *op++ = *ip++; + *op++ = *ip++; + *op++ = 0xff; + } + break; - if (fp != NULL) { - fclose(fp); - } - if (result < 0) { - if (surface) { - free(surface); - } + case 4: + // copy RGBA to RGBX + memcpy(output_row, input_row, width*4); + break; } - return result; } -int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurface) { - char resPath[256]; +int res_create_display_surface(const char* name, gr_surface* pSurface) { + gr_surface surface = NULL; int result = 0; - unsigned char header[8]; png_structp png_ptr = NULL; png_infop info_ptr = NULL; - int i; - gr_surface* surface = NULL; + png_uint_32 width, height; + png_byte channels; *pSurface = NULL; - *frames = -1; - snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name); - resPath[sizeof(resPath)-1] = '\0'; - FILE* fp = fopen(resPath, "rb"); - if (fp == NULL) { - result = -1; - goto exit; - } - - size_t bytesRead = fread(header, 1, sizeof(header), fp); - if (bytesRead != sizeof(header)) { - result = -2; - goto exit; - } - - if (png_sig_cmp(header, 0, sizeof(header))) { - result = -3; - goto exit; - } + result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels); + if (result < 0) return result; - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) { - result = -4; + surface = init_display_surface(width, height); + if (surface == NULL) { + result = -8; goto exit; } - info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - result = -5; - goto exit; + unsigned char* p_row = malloc(width * 4); + unsigned int y; + for (y = 0; y < height; ++y) { + png_read_row(png_ptr, p_row, NULL); + transform_rgb_to_draw(p_row, surface->data + y * surface->row_bytes, channels, width); } + free(p_row); - if (setjmp(png_jmpbuf(png_ptr))) { - result = -6; - goto exit; - } + *pSurface = surface; - png_init_io(png_ptr, fp); - png_set_sig_bytes(png_ptr, sizeof(header)); - png_read_info(png_ptr, info_ptr); + exit: + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + if (result < 0 && surface != NULL) free(surface); + return result; +} - int color_type, bit_depth; +int res_create_multi_display_surface(const char* name, int* frames, gr_surface** pSurface) { + gr_surface* surface = NULL; + int result = 0; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; png_uint_32 width, height; - png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, - &color_type, NULL, NULL, NULL); + png_byte channels; + int i; - int channels = png_get_channels(png_ptr, info_ptr); + *pSurface = NULL; + *frames = -1; - if (!(bit_depth <= 8 && - ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) || - (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE || - color_type == PNG_COLOR_TYPE_GRAY))))) { - return -7; - goto exit; - } + result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels); + if (result < 0) return result; *frames = 1; png_textp text; @@ -257,67 +265,35 @@ int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurfac goto exit; } - size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width; - size_t pixelSize = stride * height / *frames; - surface = malloc(*frames * sizeof(gr_surface)); if (surface == NULL) { result = -8; goto exit; } for (i = 0; i < *frames; ++i) { - surface[i] = malloc_surface(pixelSize); - surface[i]->width = width; - surface[i]->height = height / *frames; - surface[i]->row_bytes = stride; - surface[i]->pixel_bytes = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4); - } - - int alpha = (channels == 4); - png_set_expand(png_ptr); - if (color_type == PNG_COLOR_TYPE_GRAY) { - alpha = 1; + surface[i] = init_display_surface(width, height / *frames); + if (surface[i] == NULL) { + result = -8; + goto exit; + } } - png_uint_32 y; - if (channels == 3 || (channels == 1 && !alpha)) { - for (y = 0; y < height; ++y) { - int fy = y / *frames; - int fr = y % *frames; - unsigned char* pRow = surface[fr]->data + fy * 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 r = pRow[sx]; - unsigned char g = pRow[sx + 1]; - 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 + 3] = a; - } - } - } else { - for (y = 0; y < height; ++y) { - int fy = y / *frames; - int fr = y % *frames; - unsigned char* pRow = surface[fr]->data + fy * stride; - png_read_row(png_ptr, pRow, NULL); - } + unsigned char* p_row = malloc(width * 4); + unsigned int y; + for (y = 0; y < height; ++y) { + png_read_row(png_ptr, p_row, NULL); + int frame = y % *frames; + unsigned char* out_row = surface[frame]->data + + (y / *frames) * surface[frame]->row_bytes; + transform_rgb_to_draw(p_row, out_row, channels, width); } + free(p_row); *pSurface = (gr_surface*) surface; exit: png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - if (fp != NULL) { - fclose(fp); - } if (result < 0) { if (surface) { for (i = 0; i < *frames; ++i) { @@ -329,7 +305,50 @@ exit: return result; } -static int matches_locale(const char* loc) { +int res_create_alpha_surface(const char* name, gr_surface* pSurface) { + gr_surface surface = NULL; + int result = 0; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_uint_32 width, height; + png_byte channels; + + *pSurface = NULL; + + result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels); + if (result < 0) return result; + + if (channels != 1) { + result = -7; + goto exit; + } + + surface = malloc_surface(width * height); + if (surface == NULL) { + result = -8; + goto exit; + } + surface->width = width; + surface->height = height; + surface->row_bytes = width; + surface->pixel_bytes = 1; + + unsigned char* p_row; + unsigned int y; + for (y = 0; y < height; ++y) { + p_row = surface->data + y * surface->row_bytes; + png_read_row(png_ptr, p_row, NULL); + } + + *pSurface = surface; + + exit: + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + if (result < 0 && surface != NULL) free(surface); + return result; +} + +static int matches_locale(const char* loc, const char* locale) { if (locale == NULL) return 0; if (strcmp(loc, locale) == 0) return 1; @@ -346,70 +365,35 @@ static int matches_locale(const char* loc) { return (strncmp(locale, loc, i) == 0 && locale[i] == '_'); } -int res_create_localized_surface(const char* name, gr_surface* pSurface) { - char resPath[256]; +int res_create_localized_alpha_surface(const char* name, + const char* locale, + gr_surface* pSurface) { gr_surface surface = NULL; int result = 0; - unsigned char header[8]; png_structp png_ptr = NULL; png_infop info_ptr = NULL; + png_uint_32 width, height; + png_byte channels; *pSurface = NULL; - snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name); - resPath[sizeof(resPath)-1] = '\0'; - FILE* fp = fopen(resPath, "rb"); - if (fp == NULL) { - result = -1; - goto exit; - } - - size_t bytesRead = fread(header, 1, sizeof(header), fp); - if (bytesRead != sizeof(header)) { - result = -2; - goto exit; - } - - if (png_sig_cmp(header, 0, sizeof(header))) { - result = -3; - goto exit; - } - - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) { - result = -4; - goto exit; - } - - info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - result = -5; - goto exit; - } - - if (setjmp(png_jmpbuf(png_ptr))) { - result = -6; + if (locale == NULL) { + surface = malloc_surface(0); + surface->width = 0; + surface->height = 0; + surface->row_bytes = 0; + surface->pixel_bytes = 1; goto exit; } - png_init_io(png_ptr, fp); - png_set_sig_bytes(png_ptr, sizeof(header)); - png_read_info(png_ptr, info_ptr); - - int color_type, bit_depth; - png_uint_32 width, height; - png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, - &color_type, NULL, NULL, NULL); - int channels = png_get_channels(png_ptr, info_ptr); + result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels); + if (result < 0) return result; - if (!(bit_depth <= 8 && - (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) { - return -7; + if (channels != 1) { + result = -7; goto exit; } - png_set_expand(png_ptr); - unsigned char* row = malloc(width); png_uint_32 y; for (y = 0; y < height; ++y) { @@ -419,7 +403,7 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) { int len = row[4]; char* loc = (char*)row+5; - if (y+1+h >= height || matches_locale(loc)) { + if (y+1+h >= height || matches_locale(loc, locale)) { printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y); surface = malloc_surface(w*h); @@ -427,8 +411,6 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) { result = -8; goto exit; } - unsigned char* pData = surface->data; - surface->width = w; surface->height = h; surface->row_bytes = w; @@ -437,7 +419,7 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) { int i; for (i = 0; i < h; ++i, ++y) { png_read_row(png_ptr, row, NULL); - memcpy(pData + i*w, row, w); + memcpy(surface->data + i*w, row, w); } *pSurface = (gr_surface) surface; @@ -452,15 +434,7 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) { exit: png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - - if (fp != NULL) { - fclose(fp); - } - if (result < 0) { - if (surface) { - free(surface); - } - } + if (result < 0 && surface != NULL) free(surface); return result; } diff --git a/recovery.cpp b/recovery.cpp index 448f315b0..e956ed711 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -974,8 +974,8 @@ main(int argc, char **argv) { ui = device->GetUI(); gCurrentUI = ui; - ui->Init(); ui->SetLocale(locale); + ui->Init(); ui->SetBackground(RecoveryUI::NONE); if (show_text) ui->ShowText(true); diff --git a/screen_ui.cpp b/screen_ui.cpp index 589c935dc..656f72445 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -52,6 +52,7 @@ static double now() { ScreenRecoveryUI::ScreenRecoveryUI() : currentIcon(NONE), installingFrame(0), + locale(NULL), rtl_locale(false), progressBarType(EMPTY), progressScopeStart(0), @@ -306,21 +307,21 @@ void ScreenRecoveryUI::progress_loop() { } void ScreenRecoveryUI::LoadBitmap(const char* filename, gr_surface* surface) { - int result = res_create_surface(filename, surface); + int result = res_create_display_surface(filename, surface); if (result < 0) { LOGE("missing bitmap %s\n(Code %d)\n", filename, result); } } void ScreenRecoveryUI::LoadBitmapArray(const char* filename, int* frames, gr_surface** surface) { - int result = res_create_multi_surface(filename, frames, surface); + int result = res_create_multi_display_surface(filename, frames, surface); if (result < 0) { LOGE("missing bitmap %s\n(Code %d)\n", filename, result); } } void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, gr_surface* surface) { - int result = res_create_localized_surface(filename, surface); + int result = res_create_localized_alpha_surface(filename, locale, surface); if (result < 0) { LOGE("missing bitmap %s\n(Code %d)\n", filename, result); } @@ -360,8 +361,9 @@ void ScreenRecoveryUI::Init() RecoveryUI::Init(); } -void ScreenRecoveryUI::SetLocale(const char* locale) { - if (locale) { +void ScreenRecoveryUI::SetLocale(const char* new_locale) { + if (new_locale) { + this->locale = new_locale; char* lang = strdup(locale); for (char* p = lang; *p; ++p) { if (*p == '_') { @@ -380,6 +382,8 @@ void ScreenRecoveryUI::SetLocale(const char* locale) { rtl_locale = true; } free(lang); + } else { + new_locale = NULL; } } diff --git a/screen_ui.h b/screen_ui.h index ada006d1e..14b91385d 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -64,6 +64,7 @@ class ScreenRecoveryUI : public RecoveryUI { private: Icon currentIcon; int installingFrame; + const char* locale; bool rtl_locale; pthread_mutex_t updateMutex; -- cgit v1.2.3 From 76b245c24c12feec2381c423de2f7ffe4b78d7f0 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Mon, 17 Mar 2014 15:35:52 -0700 Subject: minzip: 64 bit build issue Regression - verification and extraction on memory, not files Bug: 12188746 Change-Id: Ib6facc4aff6be3a31a7d184ef1c493fdd4012c21 --- minzip/SysUtil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c index c046a8cf2..ac6f5c33f 100644 --- a/minzip/SysUtil.c +++ b/minzip/SysUtil.c @@ -103,7 +103,7 @@ static int sysMapBlockFile(FILE* mapf, MemMapping* pMap) } } - if (fscanf(mapf, "%d %d\n%d\n", &size, &blksize, &range_count) != 3) { + if (fscanf(mapf, "%zu %u\n%u\n", &size, &blksize, &range_count) != 3) { LOGW("failed to parse block map header\n"); return -1; } -- cgit v1.2.3 From a388a769760af4d8999558f38f81dafa63cc0bfb Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 17 Mar 2014 16:51:47 -0700 Subject: fix error log statement We were printing the address of channels instead of the value there. Change-Id: I652340c7c1a0abaf01af555e54d2c0366d21ad78 --- minui/resources.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minui/resources.c b/minui/resources.c index 69fd14bee..a0c621b71 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -110,7 +110,7 @@ static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr, *channels = 3; } else { fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n", - bit_depth, (int) channels, color_type); + bit_depth, *channels, color_type); result = -7; goto exit; } -- cgit v1.2.3 From b1d1263453de7f0f155628b91658a7cccb2e95fd Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 18 Mar 2014 10:32:12 -0700 Subject: add --shutdown_after option to recovery The "--shutdown_after" option causes recovery to power down the device on completion rather than rebooting. Removes the last vestiges of the "--previous_runs" argument, which doesn't seem to be used for anything. Change-Id: I465eda2ef59d367e2b1c79a8dc69831263c69a4d --- recovery.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index e956ed711..e852ef87a 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -56,6 +56,7 @@ static const struct option OPTIONS[] = { { "show_text", no_argument, NULL, 't' }, { "just_exit", no_argument, NULL, 'x' }, { "locale", required_argument, NULL, 'l' }, + { "shutdown_after", no_argument, NULL, 'p' }, { NULL, 0, NULL, 0 }, }; @@ -942,16 +943,15 @@ main(int argc, char **argv) { rotate_last_logs(10); get_args(&argc, &argv); - int previous_runs = 0; const char *send_intent = NULL; const char *update_package = NULL; int wipe_data = 0, wipe_cache = 0, show_text = 0; bool just_exit = false; + bool shutdown_after = false; int arg; while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) { switch (arg) { - case 'p': previous_runs = atoi(optarg); break; case 's': send_intent = optarg; break; case 'u': update_package = optarg; break; case 'w': wipe_data = wipe_cache = 1; break; @@ -959,6 +959,7 @@ main(int argc, char **argv) { case 't': show_text = 1; break; case 'x': just_exit = true; break; case 'l': locale = optarg; break; + case 'p': shutdown_after = true; break; case '?': LOGE("Invalid command argument\n"); continue; @@ -1061,7 +1062,12 @@ main(int argc, char **argv) { // Otherwise, get ready to boot the main system... finish_recovery(send_intent); - ui->Print("Rebooting...\n"); - property_set(ANDROID_RB_PROPERTY, "reboot,"); + if (shutdown_after) { + ui->Print("Shutting down...\n"); + property_set(ANDROID_RB_PROPERTY, "shutdown,"); + } else { + ui->Print("Rebooting...\n"); + property_set(ANDROID_RB_PROPERTY, "reboot,"); + } return EXIT_SUCCESS; } -- cgit v1.2.3 From bacf9b4558c69e918d9c05b6f111bbb223829829 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 18 Mar 2014 10:32:12 -0700 Subject: add --shutdown_after option to recovery The "--shutdown_after" option causes recovery to power down the device on completion rather than rebooting. Removes the last vestiges of the "--previous_runs" argument, which doesn't seem to be used for anything. Change-Id: I465eda2ef59d367e2b1c79a8dc69831263c69a4d Conflicts: recovery.cpp --- recovery.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index 43cd9dafe..fdb9095be 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -57,6 +57,7 @@ static const struct option OPTIONS[] = { { "just_exit", no_argument, NULL, 'x' }, { "locale", required_argument, NULL, 'l' }, { "stages", required_argument, NULL, 'g' }, + { "shutdown_after", no_argument, NULL, 'p' }, { NULL, 0, NULL, 0 }, }; @@ -945,16 +946,15 @@ main(int argc, char **argv) { rotate_last_logs(10); get_args(&argc, &argv); - int previous_runs = 0; const char *send_intent = NULL; const char *update_package = NULL; int wipe_data = 0, wipe_cache = 0, show_text = 0; bool just_exit = false; + bool shutdown_after = false; int arg; while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) { switch (arg) { - case 'p': previous_runs = atoi(optarg); break; case 's': send_intent = optarg; break; case 'u': update_package = optarg; break; case 'w': wipe_data = wipe_cache = 1; break; @@ -970,6 +970,7 @@ main(int argc, char **argv) { } break; } + case 'p': shutdown_after = true; break; case '?': LOGE("Invalid command argument\n"); continue; @@ -1079,7 +1080,12 @@ main(int argc, char **argv) { // Otherwise, get ready to boot the main system... finish_recovery(send_intent); - ui->Print("Rebooting...\n"); - property_set(ANDROID_RB_PROPERTY, "reboot,"); + if (shutdown_after) { + ui->Print("Shutting down...\n"); + property_set(ANDROID_RB_PROPERTY, "shutdown,"); + } else { + ui->Print("Rebooting...\n"); + property_set(ANDROID_RB_PROPERTY, "reboot,"); + } return EXIT_SUCCESS; } -- cgit v1.2.3 From 2605dec597f7ebabf31b7e9430f19ab888b2919a Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Wed, 19 Mar 2014 15:30:25 -0700 Subject: recovery: 64 bit build issues Change-Id: Ie88c49dea13cce5f4eb428e97f5a0956f2656a30 --- uncrypt/uncrypt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c index 7c2d99477..24d1ffc2a 100644 --- a/uncrypt/uncrypt.c +++ b/uncrypt/uncrypt.c @@ -189,10 +189,10 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de return -1; } - printf(" block size: %ld bytes\n", sb.st_blksize); + printf(" block size: %ld bytes\n", (long)sb.st_blksize); int blocks = ((sb.st_size-1) / sb.st_blksize) + 1; - printf(" file size: %lld bytes, %d blocks\n", sb.st_size, blocks); + printf(" file size: %lld bytes, %d blocks\n", (long long)sb.st_size, blocks); int* ranges; int range_alloc = 1; @@ -201,7 +201,7 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de ranges[0] = -1; ranges[1] = -1; - fprintf(mapf, "%s\n%lld %lu\n", blk_dev, sb.st_size, sb.st_blksize); + fprintf(mapf, "%s\n%lld %lu\n", blk_dev, (long long)sb.st_size, (unsigned long)sb.st_blksize); unsigned char* buffers[WINDOW_SIZE]; int i; -- cgit v1.2.3 From 577a1304352c12d968c11a667f50cd45498cae17 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 20 Mar 2014 08:27:01 -0700 Subject: restore minui support for paletted images with < 8 bits The PNG image loaders should support images that use palettes requiring fewer than 8 bits. Change-Id: Ibcb0ff6f04aea8de54b03c0efa7a79e9b3635146 --- minui/resources.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minui/resources.c b/minui/resources.c index a0c621b71..2bae4ded0 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -101,7 +101,7 @@ static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr, } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_GRAY) { // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray. png_set_expand_gray_1_2_4_to_8(*png_ptr); - } else if (bit_depth == 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE) { + } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE) { // paletted images: expand to 8-bit RGB. Note that we DON'T // currently expand the tRNS chunk (if any) to an alpha // channel, because minui doesn't support alpha channels in -- cgit v1.2.3 From 02abde50851f66196c74d215150014f1378cb853 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 1 Apr 2014 09:45:24 -0700 Subject: remove DefaultDevice's UI subclass RecoveryUI (which is the superclass of ScreenRecoveryUI), provides a basic CheckKey method that is useful for devices that have KEY_POWER, KEY_VOLUMEUP, and KEY_VOLUMEDOWN. Stop overriding it with a less featureful method; with this no recovery UI customization is needed for most handheld devices (phones, tablets, etc.). Change-Id: I7d57cfaaef79afea8af4fc3dbc570afc61aeb5bc --- default_device.cpp | 13 ++----------- ui.cpp | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/default_device.cpp b/default_device.cpp index 648eaec4e..1f181318e 100644 --- a/default_device.cpp +++ b/default_device.cpp @@ -31,20 +31,10 @@ static const char* ITEMS[] = {"reboot system now", "wipe cache partition", NULL }; -class DefaultUI : public ScreenRecoveryUI { - public: - virtual KeyAction CheckKey(int key) { - if (key == KEY_HOME) { - return TOGGLE; - } - return ENQUEUE; - } -}; - class DefaultDevice : public Device { public: DefaultDevice() : - ui(new DefaultUI) { + ui(new ScreenRecoveryUI) { } RecoveryUI* GetUI() { return ui; } @@ -61,6 +51,7 @@ class DefaultDevice : public Device { return kHighlightUp; case KEY_ENTER: + case KEY_POWER: return kInvokeItem; } } diff --git a/ui.cpp b/ui.cpp index 67a2500bb..091012f29 100644 --- a/ui.cpp +++ b/ui.cpp @@ -276,7 +276,7 @@ void RecoveryUI::FlushKeys() { // - Press power seven times in a row to reboot. // - Alternate vol-up and vol-down seven times to mount /system. RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) { - if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) { + if ((IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) || key == KEY_HOME) { return TOGGLE; } -- cgit v1.2.3 From 8d9d3d5cbe240d09db10d08956d152dce934e892 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 1 Apr 2014 13:20:23 -0700 Subject: add reboot-to-bootloader and power down options to recovery menu Useful when debugging or developing for recovery. Change-Id: Ic3ab42d5e848ad3488f1c575339b55e45c8a024b --- default_device.cpp | 4 ++++ device.h | 3 ++- recovery.cpp | 55 +++++++++++++++++++++++++++++++++++++----------------- 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/default_device.cpp b/default_device.cpp index 1f181318e..a25f05f8e 100644 --- a/default_device.cpp +++ b/default_device.cpp @@ -29,6 +29,8 @@ static const char* ITEMS[] = {"reboot system now", "apply update from ADB", "wipe data/factory reset", "wipe cache partition", + "reboot to bootloader", + "power down", NULL }; class DefaultDevice : public Device { @@ -65,6 +67,8 @@ class DefaultDevice : public Device { case 1: return APPLY_ADB_SIDELOAD; case 2: return WIPE_DATA; case 3: return WIPE_CACHE; + case 4: return REBOOT_BOOTLOADER; + case 5: return SHUTDOWN; default: return NO_ACTION; } } diff --git a/device.h b/device.h index 583de75ef..df71377c1 100644 --- a/device.h +++ b/device.h @@ -66,7 +66,8 @@ class Device { virtual int HandleMenuKey(int key, int visible) = 0; enum BuiltinAction { NO_ACTION, REBOOT, APPLY_EXT, APPLY_CACHE, - APPLY_ADB_SIDELOAD, WIPE_DATA, WIPE_CACHE }; + APPLY_ADB_SIDELOAD, WIPE_DATA, WIPE_CACHE, + REBOOT_BOOTLOADER, SHUTDOWN }; // Perform a recovery action selected from the menu. // 'menu_position' will be the item number of the selected menu diff --git a/recovery.cpp b/recovery.cpp index 8d3731540..09af5f8b1 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -771,7 +771,10 @@ wipe_data(int confirm, Device* device) { ui->Print("Data wipe complete.\n"); } -static void +// Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION +// means to take the default, which is to reboot or shutdown depending +// on if the --shutdown_after flag was passed to recovery. +static Device::BuiltinAction prompt_and_wait(Device* device, int status) { const char* const* headers = prepend_title(device->GetMenuHeaders()); @@ -795,23 +798,28 @@ prompt_and_wait(Device* device, int status) { // 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->InvokeMenuItem(chosen_item); + Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item); int wipe_cache; - switch (chosen_item) { + switch (chosen_action) { + case Device::NO_ACTION: + break; + case Device::REBOOT: - return; + case Device::SHUTDOWN: + case Device::REBOOT_BOOTLOADER: + return chosen_action; case Device::WIPE_DATA: wipe_data(ui->IsTextVisible(), device); - if (!ui->IsTextVisible()) return; + if (!ui->IsTextVisible()) return Device::NO_ACTION; break; case Device::WIPE_CACHE: ui->Print("\n-- Wiping cache...\n"); erase_volume("/cache"); ui->Print("Cache wipe complete.\n"); - if (!ui->IsTextVisible()) return; + if (!ui->IsTextVisible()) return Device::NO_ACTION; break; case Device::APPLY_EXT: @@ -829,7 +837,7 @@ prompt_and_wait(Device* device, int status) { ui->SetBackground(RecoveryUI::ERROR); ui->Print("Installation aborted.\n"); } else if (!ui->IsTextVisible()) { - return; // reboot if logs aren't visible + return Device::NO_ACTION; // reboot if logs aren't visible } else { ui->Print("\nInstall from sdcard complete.\n"); } @@ -852,7 +860,7 @@ prompt_and_wait(Device* device, int status) { ui->SetBackground(RecoveryUI::ERROR); ui->Print("Installation aborted.\n"); } else if (!ui->IsTextVisible()) { - return; // reboot if logs aren't visible + return Device::NO_ACTION; // reboot if logs aren't visible } else { ui->Print("\nInstall from cache complete.\n"); } @@ -867,7 +875,7 @@ prompt_and_wait(Device* device, int status) { ui->Print("Installation aborted.\n"); copy_logs(); } else if (!ui->IsTextVisible()) { - return; // reboot if logs aren't visible + return Device::NO_ACTION; // reboot if logs aren't visible } else { ui->Print("\nInstall from ADB complete.\n"); } @@ -1074,18 +1082,31 @@ main(int argc, char **argv) { copy_logs(); ui->SetBackground(RecoveryUI::ERROR); } + Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT; if (status != INSTALL_SUCCESS || ui->IsTextVisible()) { - prompt_and_wait(device, status); + Device::BuiltinAction temp = prompt_and_wait(device, status); + if (temp != Device::NO_ACTION) after = temp; } - // Otherwise, get ready to boot the main system... + // Save logs and clean up before rebooting or shutting down. finish_recovery(send_intent); - if (shutdown_after) { - ui->Print("Shutting down...\n"); - property_set(ANDROID_RB_PROPERTY, "shutdown,"); - } else { - ui->Print("Rebooting...\n"); - property_set(ANDROID_RB_PROPERTY, "reboot,"); + + switch (after) { + case Device::SHUTDOWN: + ui->Print("Shutting down...\n"); + property_set(ANDROID_RB_PROPERTY, "shutdown,"); + break; + + case Device::REBOOT_BOOTLOADER: + ui->Print("Rebooting to bootloader...\n"); + property_set(ANDROID_RB_PROPERTY, "reboot,bootloader"); + break; + + default: + ui->Print("Rebooting...\n"); + property_set(ANDROID_RB_PROPERTY, "reboot,"); + break; } + sleep(5); // should reboot before this finishes return EXIT_SUCCESS; } -- cgit v1.2.3 From 9551cf912180665a85f515c16d6412bb8ea2bf98 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 4 Apr 2014 13:48:33 -0700 Subject: make density-specific recovery assets Provide different recovery UI resources for different display densities. Right now only the text images and the progress bars are rescaled; the main icon will get scaled when it's updated for QP. Lightly quantum-ify the rest of the recovery interface. (Light background, progress bars, etc.) Change-Id: Ia639c4ce8534b01bc843524efbc4b040c1cf38b3 --- res-hdpi/images/erasing_text.png | Bin 0 -> 20528 bytes res-hdpi/images/error_text.png | Bin 0 -> 11684 bytes res-hdpi/images/icon_error.png | Bin 0 -> 19306 bytes res-hdpi/images/icon_installing.png | Bin 0 -> 118562 bytes res-hdpi/images/installing_text.png | Bin 0 -> 40946 bytes res-hdpi/images/no_command_text.png | Bin 0 -> 25013 bytes res-hdpi/images/progress_empty.png | Bin 0 -> 81 bytes res-hdpi/images/progress_fill.png | Bin 0 -> 83 bytes res-hdpi/images/stage_empty.png | Bin 0 -> 322 bytes res-hdpi/images/stage_fill.png | Bin 0 -> 258 bytes res-mdpi/images/erasing_text.png | Bin 0 -> 12208 bytes res-mdpi/images/error_text.png | Bin 0 -> 7202 bytes res-mdpi/images/icon_error.png | Bin 0 -> 19306 bytes res-mdpi/images/icon_installing.png | Bin 0 -> 118562 bytes res-mdpi/images/installing_text.png | Bin 0 -> 24575 bytes res-mdpi/images/no_command_text.png | Bin 0 -> 14168 bytes res-mdpi/images/progress_empty.png | Bin 0 -> 74 bytes res-mdpi/images/progress_fill.png | Bin 0 -> 83 bytes res-mdpi/images/stage_empty.png | Bin 0 -> 322 bytes res-mdpi/images/stage_fill.png | Bin 0 -> 258 bytes res-xhdpi/images/erasing_text.png | Bin 0 -> 29674 bytes res-xhdpi/images/error_text.png | Bin 0 -> 16599 bytes res-xhdpi/images/icon_error.png | Bin 0 -> 19306 bytes res-xhdpi/images/icon_installing.png | Bin 0 -> 118562 bytes res-xhdpi/images/installing_text.png | Bin 0 -> 63220 bytes res-xhdpi/images/no_command_text.png | Bin 0 -> 37222 bytes res-xhdpi/images/progress_empty.png | Bin 0 -> 84 bytes res-xhdpi/images/progress_fill.png | Bin 0 -> 84 bytes res-xhdpi/images/stage_empty.png | Bin 0 -> 322 bytes res-xhdpi/images/stage_fill.png | Bin 0 -> 258 bytes res-xxhdpi/images/erasing_text.png | Bin 0 -> 52598 bytes res-xxhdpi/images/error_text.png | Bin 0 -> 27839 bytes res-xxhdpi/images/icon_error.png | Bin 0 -> 19306 bytes res-xxhdpi/images/icon_installing.png | Bin 0 -> 118562 bytes res-xxhdpi/images/installing_text.png | Bin 0 -> 133331 bytes res-xxhdpi/images/no_command_text.png | Bin 0 -> 63887 bytes res-xxhdpi/images/progress_empty.png | Bin 0 -> 86 bytes res-xxhdpi/images/progress_fill.png | Bin 0 -> 86 bytes res-xxhdpi/images/stage_empty.png | Bin 0 -> 322 bytes res-xxhdpi/images/stage_fill.png | Bin 0 -> 258 bytes res-xxxhdpi/images/erasing_text.png | Bin 0 -> 85500 bytes res-xxxhdpi/images/error_text.png | Bin 0 -> 40216 bytes res-xxxhdpi/images/icon_error.png | Bin 0 -> 19306 bytes res-xxxhdpi/images/icon_installing.png | Bin 0 -> 118562 bytes res-xxxhdpi/images/installing_text.png | Bin 0 -> 184958 bytes res-xxxhdpi/images/no_command_text.png | Bin 0 -> 109746 bytes res-xxxhdpi/images/progress_empty.png | Bin 0 -> 90 bytes res-xxxhdpi/images/progress_fill.png | Bin 0 -> 90 bytes res-xxxhdpi/images/stage_empty.png | Bin 0 -> 322 bytes res-xxxhdpi/images/stage_fill.png | Bin 0 -> 258 bytes res/images/erasing_text.png | Bin 31491 -> 0 bytes res/images/error_text.png | Bin 17504 -> 0 bytes res/images/icon_error.png | Bin 19306 -> 0 bytes res/images/icon_installing.png | Bin 118562 -> 0 bytes res/images/installing_text.png | Bin 66592 -> 0 bytes res/images/no_command_text.png | Bin 40632 -> 0 bytes res/images/progress_empty.png | Bin 118 -> 0 bytes res/images/progress_fill.png | Bin 404 -> 0 bytes res/images/stage_empty.png | Bin 322 -> 0 bytes res/images/stage_fill.png | Bin 258 -> 0 bytes screen_ui.cpp | 29 +++++++++++++---------------- screen_ui.h | 3 +++ 62 files changed, 16 insertions(+), 16 deletions(-) create mode 100644 res-hdpi/images/erasing_text.png create mode 100644 res-hdpi/images/error_text.png create mode 100644 res-hdpi/images/icon_error.png create mode 100644 res-hdpi/images/icon_installing.png create mode 100644 res-hdpi/images/installing_text.png create mode 100644 res-hdpi/images/no_command_text.png create mode 100644 res-hdpi/images/progress_empty.png create mode 100644 res-hdpi/images/progress_fill.png create mode 100644 res-hdpi/images/stage_empty.png create mode 100644 res-hdpi/images/stage_fill.png create mode 100644 res-mdpi/images/erasing_text.png create mode 100644 res-mdpi/images/error_text.png create mode 100644 res-mdpi/images/icon_error.png create mode 100644 res-mdpi/images/icon_installing.png create mode 100644 res-mdpi/images/installing_text.png create mode 100644 res-mdpi/images/no_command_text.png create mode 100644 res-mdpi/images/progress_empty.png create mode 100644 res-mdpi/images/progress_fill.png create mode 100644 res-mdpi/images/stage_empty.png create mode 100644 res-mdpi/images/stage_fill.png create mode 100644 res-xhdpi/images/erasing_text.png create mode 100644 res-xhdpi/images/error_text.png create mode 100644 res-xhdpi/images/icon_error.png create mode 100644 res-xhdpi/images/icon_installing.png create mode 100644 res-xhdpi/images/installing_text.png create mode 100644 res-xhdpi/images/no_command_text.png create mode 100644 res-xhdpi/images/progress_empty.png create mode 100644 res-xhdpi/images/progress_fill.png create mode 100644 res-xhdpi/images/stage_empty.png create mode 100644 res-xhdpi/images/stage_fill.png create mode 100644 res-xxhdpi/images/erasing_text.png create mode 100644 res-xxhdpi/images/error_text.png create mode 100644 res-xxhdpi/images/icon_error.png create mode 100644 res-xxhdpi/images/icon_installing.png create mode 100644 res-xxhdpi/images/installing_text.png create mode 100644 res-xxhdpi/images/no_command_text.png create mode 100644 res-xxhdpi/images/progress_empty.png create mode 100644 res-xxhdpi/images/progress_fill.png create mode 100644 res-xxhdpi/images/stage_empty.png create mode 100644 res-xxhdpi/images/stage_fill.png create mode 100644 res-xxxhdpi/images/erasing_text.png create mode 100644 res-xxxhdpi/images/error_text.png create mode 100644 res-xxxhdpi/images/icon_error.png create mode 100644 res-xxxhdpi/images/icon_installing.png create mode 100644 res-xxxhdpi/images/installing_text.png create mode 100644 res-xxxhdpi/images/no_command_text.png create mode 100644 res-xxxhdpi/images/progress_empty.png create mode 100644 res-xxxhdpi/images/progress_fill.png create mode 100644 res-xxxhdpi/images/stage_empty.png create mode 100644 res-xxxhdpi/images/stage_fill.png delete mode 100644 res/images/erasing_text.png delete mode 100644 res/images/error_text.png delete mode 100644 res/images/icon_error.png delete mode 100644 res/images/icon_installing.png delete mode 100644 res/images/installing_text.png delete mode 100644 res/images/no_command_text.png delete mode 100644 res/images/progress_empty.png delete mode 100644 res/images/progress_fill.png delete mode 100644 res/images/stage_empty.png delete mode 100644 res/images/stage_fill.png diff --git a/res-hdpi/images/erasing_text.png b/res-hdpi/images/erasing_text.png new file mode 100644 index 000000000..774244c84 Binary files /dev/null and b/res-hdpi/images/erasing_text.png differ diff --git a/res-hdpi/images/error_text.png b/res-hdpi/images/error_text.png new file mode 100644 index 000000000..64a57ec4e Binary files /dev/null and b/res-hdpi/images/error_text.png differ diff --git a/res-hdpi/images/icon_error.png b/res-hdpi/images/icon_error.png new file mode 100644 index 000000000..cb3d1ab22 Binary files /dev/null and b/res-hdpi/images/icon_error.png differ diff --git a/res-hdpi/images/icon_installing.png b/res-hdpi/images/icon_installing.png new file mode 100644 index 000000000..c2c020162 Binary files /dev/null and b/res-hdpi/images/icon_installing.png differ diff --git a/res-hdpi/images/installing_text.png b/res-hdpi/images/installing_text.png new file mode 100644 index 000000000..33b54f1bf Binary files /dev/null and b/res-hdpi/images/installing_text.png differ diff --git a/res-hdpi/images/no_command_text.png b/res-hdpi/images/no_command_text.png new file mode 100644 index 000000000..9927ecb6d Binary files /dev/null and b/res-hdpi/images/no_command_text.png differ diff --git a/res-hdpi/images/progress_empty.png b/res-hdpi/images/progress_empty.png new file mode 100644 index 000000000..b29d9be0c Binary files /dev/null and b/res-hdpi/images/progress_empty.png differ diff --git a/res-hdpi/images/progress_fill.png b/res-hdpi/images/progress_fill.png new file mode 100644 index 000000000..4c5e66bb5 Binary files /dev/null and b/res-hdpi/images/progress_fill.png differ diff --git a/res-hdpi/images/stage_empty.png b/res-hdpi/images/stage_empty.png new file mode 100644 index 000000000..251ec1969 Binary files /dev/null and b/res-hdpi/images/stage_empty.png differ diff --git a/res-hdpi/images/stage_fill.png b/res-hdpi/images/stage_fill.png new file mode 100644 index 000000000..1ab79e862 Binary files /dev/null and b/res-hdpi/images/stage_fill.png differ diff --git a/res-mdpi/images/erasing_text.png b/res-mdpi/images/erasing_text.png new file mode 100644 index 000000000..fd86c3f6e Binary files /dev/null and b/res-mdpi/images/erasing_text.png differ diff --git a/res-mdpi/images/error_text.png b/res-mdpi/images/error_text.png new file mode 100644 index 000000000..f1b44c9b3 Binary files /dev/null and b/res-mdpi/images/error_text.png differ diff --git a/res-mdpi/images/icon_error.png b/res-mdpi/images/icon_error.png new file mode 100644 index 000000000..cb3d1ab22 Binary files /dev/null and b/res-mdpi/images/icon_error.png differ diff --git a/res-mdpi/images/icon_installing.png b/res-mdpi/images/icon_installing.png new file mode 100644 index 000000000..c2c020162 Binary files /dev/null and b/res-mdpi/images/icon_installing.png differ diff --git a/res-mdpi/images/installing_text.png b/res-mdpi/images/installing_text.png new file mode 100644 index 000000000..064b2a317 Binary files /dev/null and b/res-mdpi/images/installing_text.png differ diff --git a/res-mdpi/images/no_command_text.png b/res-mdpi/images/no_command_text.png new file mode 100644 index 000000000..1f29b8951 Binary files /dev/null and b/res-mdpi/images/no_command_text.png differ diff --git a/res-mdpi/images/progress_empty.png b/res-mdpi/images/progress_empty.png new file mode 100644 index 000000000..0883e0070 Binary files /dev/null and b/res-mdpi/images/progress_empty.png differ diff --git a/res-mdpi/images/progress_fill.png b/res-mdpi/images/progress_fill.png new file mode 100644 index 000000000..c223871b9 Binary files /dev/null and b/res-mdpi/images/progress_fill.png differ diff --git a/res-mdpi/images/stage_empty.png b/res-mdpi/images/stage_empty.png new file mode 100644 index 000000000..251ec1969 Binary files /dev/null and b/res-mdpi/images/stage_empty.png differ diff --git a/res-mdpi/images/stage_fill.png b/res-mdpi/images/stage_fill.png new file mode 100644 index 000000000..1ab79e862 Binary files /dev/null and b/res-mdpi/images/stage_fill.png differ diff --git a/res-xhdpi/images/erasing_text.png b/res-xhdpi/images/erasing_text.png new file mode 100644 index 000000000..f88e0e6a8 Binary files /dev/null and b/res-xhdpi/images/erasing_text.png differ diff --git a/res-xhdpi/images/error_text.png b/res-xhdpi/images/error_text.png new file mode 100644 index 000000000..c3a4cc6f8 Binary files /dev/null and b/res-xhdpi/images/error_text.png differ diff --git a/res-xhdpi/images/icon_error.png b/res-xhdpi/images/icon_error.png new file mode 100644 index 000000000..cb3d1ab22 Binary files /dev/null and b/res-xhdpi/images/icon_error.png differ diff --git a/res-xhdpi/images/icon_installing.png b/res-xhdpi/images/icon_installing.png new file mode 100644 index 000000000..c2c020162 Binary files /dev/null and b/res-xhdpi/images/icon_installing.png differ diff --git a/res-xhdpi/images/installing_text.png b/res-xhdpi/images/installing_text.png new file mode 100644 index 000000000..a4dacd0f6 Binary files /dev/null and b/res-xhdpi/images/installing_text.png differ diff --git a/res-xhdpi/images/no_command_text.png b/res-xhdpi/images/no_command_text.png new file mode 100644 index 000000000..eb34e94b3 Binary files /dev/null and b/res-xhdpi/images/no_command_text.png differ diff --git a/res-xhdpi/images/progress_empty.png b/res-xhdpi/images/progress_empty.png new file mode 100644 index 000000000..429797bf0 Binary files /dev/null and b/res-xhdpi/images/progress_empty.png differ diff --git a/res-xhdpi/images/progress_fill.png b/res-xhdpi/images/progress_fill.png new file mode 100644 index 000000000..7af35aaf1 Binary files /dev/null and b/res-xhdpi/images/progress_fill.png differ diff --git a/res-xhdpi/images/stage_empty.png b/res-xhdpi/images/stage_empty.png new file mode 100644 index 000000000..251ec1969 Binary files /dev/null and b/res-xhdpi/images/stage_empty.png differ diff --git a/res-xhdpi/images/stage_fill.png b/res-xhdpi/images/stage_fill.png new file mode 100644 index 000000000..1ab79e862 Binary files /dev/null and b/res-xhdpi/images/stage_fill.png differ diff --git a/res-xxhdpi/images/erasing_text.png b/res-xxhdpi/images/erasing_text.png new file mode 100644 index 000000000..c87fd52b4 Binary files /dev/null and b/res-xxhdpi/images/erasing_text.png differ diff --git a/res-xxhdpi/images/error_text.png b/res-xxhdpi/images/error_text.png new file mode 100644 index 000000000..486e951df Binary files /dev/null and b/res-xxhdpi/images/error_text.png differ diff --git a/res-xxhdpi/images/icon_error.png b/res-xxhdpi/images/icon_error.png new file mode 100644 index 000000000..cb3d1ab22 Binary files /dev/null and b/res-xxhdpi/images/icon_error.png differ diff --git a/res-xxhdpi/images/icon_installing.png b/res-xxhdpi/images/icon_installing.png new file mode 100644 index 000000000..c2c020162 Binary files /dev/null and b/res-xxhdpi/images/icon_installing.png differ diff --git a/res-xxhdpi/images/installing_text.png b/res-xxhdpi/images/installing_text.png new file mode 100644 index 000000000..ef6e8f3f0 Binary files /dev/null and b/res-xxhdpi/images/installing_text.png differ diff --git a/res-xxhdpi/images/no_command_text.png b/res-xxhdpi/images/no_command_text.png new file mode 100644 index 000000000..cc98bb18a Binary files /dev/null and b/res-xxhdpi/images/no_command_text.png differ diff --git a/res-xxhdpi/images/progress_empty.png b/res-xxhdpi/images/progress_empty.png new file mode 100644 index 000000000..57c823587 Binary files /dev/null and b/res-xxhdpi/images/progress_empty.png differ diff --git a/res-xxhdpi/images/progress_fill.png b/res-xxhdpi/images/progress_fill.png new file mode 100644 index 000000000..0f36f48c2 Binary files /dev/null and b/res-xxhdpi/images/progress_fill.png differ diff --git a/res-xxhdpi/images/stage_empty.png b/res-xxhdpi/images/stage_empty.png new file mode 100644 index 000000000..251ec1969 Binary files /dev/null and b/res-xxhdpi/images/stage_empty.png differ diff --git a/res-xxhdpi/images/stage_fill.png b/res-xxhdpi/images/stage_fill.png new file mode 100644 index 000000000..1ab79e862 Binary files /dev/null and b/res-xxhdpi/images/stage_fill.png differ diff --git a/res-xxxhdpi/images/erasing_text.png b/res-xxxhdpi/images/erasing_text.png new file mode 100644 index 000000000..612e7a390 Binary files /dev/null and b/res-xxxhdpi/images/erasing_text.png differ diff --git a/res-xxxhdpi/images/error_text.png b/res-xxxhdpi/images/error_text.png new file mode 100644 index 000000000..50d2fadb5 Binary files /dev/null and b/res-xxxhdpi/images/error_text.png differ diff --git a/res-xxxhdpi/images/icon_error.png b/res-xxxhdpi/images/icon_error.png new file mode 100644 index 000000000..cb3d1ab22 Binary files /dev/null and b/res-xxxhdpi/images/icon_error.png differ diff --git a/res-xxxhdpi/images/icon_installing.png b/res-xxxhdpi/images/icon_installing.png new file mode 100644 index 000000000..c2c020162 Binary files /dev/null and b/res-xxxhdpi/images/icon_installing.png differ diff --git a/res-xxxhdpi/images/installing_text.png b/res-xxxhdpi/images/installing_text.png new file mode 100644 index 000000000..9bd093bf4 Binary files /dev/null and b/res-xxxhdpi/images/installing_text.png differ diff --git a/res-xxxhdpi/images/no_command_text.png b/res-xxxhdpi/images/no_command_text.png new file mode 100644 index 000000000..6354e6a99 Binary files /dev/null and b/res-xxxhdpi/images/no_command_text.png differ diff --git a/res-xxxhdpi/images/progress_empty.png b/res-xxxhdpi/images/progress_empty.png new file mode 100644 index 000000000..ba25afb6e Binary files /dev/null and b/res-xxxhdpi/images/progress_empty.png differ diff --git a/res-xxxhdpi/images/progress_fill.png b/res-xxxhdpi/images/progress_fill.png new file mode 100644 index 000000000..83b72b27e Binary files /dev/null and b/res-xxxhdpi/images/progress_fill.png differ diff --git a/res-xxxhdpi/images/stage_empty.png b/res-xxxhdpi/images/stage_empty.png new file mode 100644 index 000000000..251ec1969 Binary files /dev/null and b/res-xxxhdpi/images/stage_empty.png differ diff --git a/res-xxxhdpi/images/stage_fill.png b/res-xxxhdpi/images/stage_fill.png new file mode 100644 index 000000000..1ab79e862 Binary files /dev/null and b/res-xxxhdpi/images/stage_fill.png differ diff --git a/res/images/erasing_text.png b/res/images/erasing_text.png deleted file mode 100644 index 441768a0c..000000000 Binary files a/res/images/erasing_text.png and /dev/null differ diff --git a/res/images/error_text.png b/res/images/error_text.png deleted file mode 100644 index 4ac6391ff..000000000 Binary files a/res/images/error_text.png and /dev/null differ diff --git a/res/images/icon_error.png b/res/images/icon_error.png deleted file mode 100644 index cb3d1ab22..000000000 Binary files a/res/images/icon_error.png and /dev/null differ diff --git a/res/images/icon_installing.png b/res/images/icon_installing.png deleted file mode 100644 index c2c020162..000000000 Binary files a/res/images/icon_installing.png and /dev/null differ diff --git a/res/images/installing_text.png b/res/images/installing_text.png deleted file mode 100644 index e1ac819e6..000000000 Binary files a/res/images/installing_text.png and /dev/null differ diff --git a/res/images/no_command_text.png b/res/images/no_command_text.png deleted file mode 100644 index a688f0935..000000000 Binary files a/res/images/no_command_text.png and /dev/null differ diff --git a/res/images/progress_empty.png b/res/images/progress_empty.png deleted file mode 100644 index 72581832c..000000000 Binary files a/res/images/progress_empty.png and /dev/null differ diff --git a/res/images/progress_fill.png b/res/images/progress_fill.png deleted file mode 100644 index becf87bdf..000000000 Binary files a/res/images/progress_fill.png and /dev/null differ diff --git a/res/images/stage_empty.png b/res/images/stage_empty.png deleted file mode 100644 index 251ec1969..000000000 Binary files a/res/images/stage_empty.png and /dev/null differ diff --git a/res/images/stage_fill.png b/res/images/stage_fill.png deleted file mode 100644 index 1ab79e862..000000000 Binary files a/res/images/stage_fill.png and /dev/null differ diff --git a/screen_ui.cpp b/screen_ui.cpp index af58643dc..afe856c37 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -87,7 +87,7 @@ ScreenRecoveryUI::ScreenRecoveryUI() : void ScreenRecoveryUI::draw_background_locked(Icon icon) { pagesIdentical = false; - gr_color(0, 0, 0, 255); + gr_color(250, 250, 250, 255); gr_clear(); if (icon) { @@ -123,7 +123,7 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) } } - gr_color(255, 255, 255, 255); + gr_color(115, 115, 115, 255); gr_texticon(textX, textY, text_surface); } } @@ -148,7 +148,7 @@ void ScreenRecoveryUI::draw_progress_locked() int dy = (3*gr_fb_height() + iconHeight - 2*height)/4; // Erase behind the progress bar (in case this was a progress-only update) - gr_color(0, 0, 0, 255); + gr_color(250, 250, 250, 255); gr_fill(dx, dy, width, height); if (progressBarType == DETERMINATE) { @@ -179,20 +179,17 @@ void ScreenRecoveryUI::draw_progress_locked() void ScreenRecoveryUI::SetColor(UIElement e) { switch (e) { case HEADER: - gr_color(247, 0, 6, 255); + gr_color(0xff, 0x57, 0x22, 255); // Quantum "Deep Orange" 500 break; case MENU: case MENU_SEL_BG: - gr_color(0, 106, 157, 255); + gr_color(0x67, 0x3a, 0xb7, 255); // Quantum "Deep Purple" 500 break; case MENU_SEL_FG: gr_color(255, 255, 255, 255); break; case LOG: - gr_color(249, 194, 0, 255); - break; - case TEXT_FILL: - gr_color(0, 0, 0, 160); + gr_color(0x3f, 0x51, 0xb5, 255); // Quantum "Indigo" 500 break; default: gr_color(255, 255, 255, 255); @@ -208,7 +205,7 @@ void ScreenRecoveryUI::draw_screen_locked() draw_background_locked(currentIcon); draw_progress_locked(); } else { - gr_color(0, 0, 0, 255); + gr_color(250, 250, 250, 255); gr_clear(); int y = 0; @@ -222,13 +219,13 @@ void ScreenRecoveryUI::draw_screen_locked() if (i == menu_top + menu_sel) { // draw the highlight bar SetColor(MENU_SEL_BG); - gr_fill(0, y-2, gr_fb_width(), y+char_height+2); + gr_fill(0, y-2+kTextYOffset, gr_fb_width(), y+char_height+2+kTextYOffset); // white text of selected item SetColor(MENU_SEL_FG); - if (menu[i][0]) gr_text(4, y, menu[i], 1); + if (menu[i][0]) gr_text(kTextXOffset, y+kTextYOffset, menu[i], 1); SetColor(MENU); } else { - if (menu[i][0]) gr_text(4, y, menu[i], i < menu_top); + if (menu[i][0]) gr_text(kTextXOffset, y+kTextYOffset, menu[i], i < menu_top); } y += char_height+4; } @@ -249,7 +246,7 @@ void ScreenRecoveryUI::draw_screen_locked() for (int ty = gr_fb_height() - char_height, count = 0; ty > y+2 && count < text_rows; ty -= char_height, ++count) { - gr_text(4, ty, text[row], 0); + gr_text(kTextXOffset, ty+kTextYOffset, text[row], 0); --row; if (row < 0) row = text_rows-1; } @@ -350,11 +347,11 @@ void ScreenRecoveryUI::Init() gr_font_size(&char_width, &char_height); text_col = text_row = 0; - text_rows = gr_fb_height() / char_height; + text_rows = (gr_fb_height() - 2 * kTextYOffset) / char_height; if (text_rows > kMaxRows) text_rows = kMaxRows; text_top = 1; - text_cols = gr_fb_width() / char_width; + text_cols = (gr_fb_width() - 2 * kTextXOffset) / char_width; if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1; backgroundIcon[NONE] = NULL; diff --git a/screen_ui.h b/screen_ui.h index 92e4795b4..2e01acdae 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -87,6 +87,9 @@ class ScreenRecoveryUI : public RecoveryUI { static const int kMaxCols = 96; static const int kMaxRows = 96; + static const int kTextXOffset = 8; + static const int kTextYOffset = 4; + // Log text overlay, displayed when a magic key is pressed char text[kMaxRows][kMaxCols]; int text_cols, text_rows; -- cgit v1.2.3 From 85ef47dd84f5bbf3fa5804f3f3a240ef736818e6 Mon Sep 17 00:00:00 2001 From: Sasha Levitskiy Date: Thu, 10 Apr 2014 17:11:34 -0700 Subject: Change filesystem add entry API call. Needs fs_mgr matching change from: Icf23e659265d71d5226d527c2b40cfbc132320ee Change-Id: I9adb470b04e4301989d128c9c3097b21b4dea431 Signed-off-by: Sasha Levitskiy --- roots.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roots.cpp b/roots.cpp index 113dba1bd..cfe133828 100644 --- a/roots.cpp +++ b/roots.cpp @@ -44,7 +44,7 @@ void load_volume_table() return; } - ret = fs_mgr_add_entry(fstab, "/tmp", "ramdisk", "ramdisk", 0); + ret = fs_mgr_add_entry(fstab, "/tmp", "ramdisk", "ramdisk"); if (ret < 0 ) { LOGE("failed to add /tmp entry to fstab\n"); fs_mgr_free_fstab(fstab); -- cgit v1.2.3 From 0e22af658c89fb53ab1215a3538bbaf10d733ad9 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Wed, 19 Feb 2014 17:41:02 -0800 Subject: recovery: Fix adb with linux 3.10 In kernel 3.10, f_adb has been removed and adbd can use functionfs instead. Mount functionfs on boot for adbd. On older kernels, mount will fail silently and adbd will revert to f_adb. Bug: 14160609 Change-Id: I5db57aaf35b35859ea88c7d0e0661d8c553e5811 --- etc/init.rc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/etc/init.rc b/etc/init.rc index 159747e2c..6e0595b44 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -28,9 +28,15 @@ on init chown root shell /tmp chmod 0775 /tmp +on fs + mkdir /dev/usb-ffs 0770 shell shell + mkdir /dev/usb-ffs/adb 0770 shell shell + mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000 + write /sys/class/android_usb/android0/enable 0 write /sys/class/android_usb/android0/idVendor 18D1 write /sys/class/android_usb/android0/idProduct D001 + write /sys/class/android_usb/android0/f_ffs/aliases adb write /sys/class/android_usb/android0/functions adb write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer} write /sys/class/android_usb/android0/iProduct ${ro.product.model} -- cgit v1.2.3 From 41909ddefbb57a9ce3c417341c1f83261e482046 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Fri, 25 Apr 2014 10:39:50 -0700 Subject: recovery: minui: add adf backend Change-Id: I1ad97b5b62bddbcb1104fa0b4e54415b793a4194 Signed-off-by: Greg Hackmann --- CleanSpec.mk | 1 + minui/Android.mk | 5 +- minui/graphics.c | 18 +++- minui/graphics.h | 1 + minui/graphics_adf.c | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 267 insertions(+), 5 deletions(-) create mode 100644 minui/graphics_adf.c diff --git a/CleanSpec.mk b/CleanSpec.mk index ecf89ae75..e2d97d42b 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -48,3 +48,4 @@ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/recovery_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libminui_intermediates/import_includes) diff --git a/minui/Android.mk b/minui/Android.mk index 5cc84ac7a..df4aac169 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -1,12 +1,15 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := graphics.c graphics_fbdev.c events.c resources.c +LOCAL_SRC_FILES := graphics.c graphics_adf.c graphics_fbdev.c events.c \ + resources.c LOCAL_C_INCLUDES +=\ external/libpng\ external/zlib +LOCAL_WHOLE_STATIC_LIBRARIES += libadf + LOCAL_MODULE := libminui # This used to compare against values in double-quotes (which are just diff --git a/minui/graphics.c b/minui/graphics.c index 32b3361e3..6049d85ca 100644 --- a/minui/graphics.c +++ b/minui/graphics.c @@ -369,10 +369,20 @@ int gr_init(void) return -1; } - gr_backend = open_fbdev(); - gr_draw = gr_backend->init(gr_backend); - if (gr_draw == NULL) { - return -1; + gr_backend = open_adf(); + if (gr_backend) { + gr_draw = gr_backend->init(gr_backend); + if (!gr_draw) { + gr_backend->exit(gr_backend); + } + } + + if (!gr_draw) { + gr_backend = open_fbdev(); + gr_draw = gr_backend->init(gr_backend); + if (gr_draw == NULL) { + return -1; + } } overscan_offset_x = gr_draw->width * overscan_percent / 100; diff --git a/minui/graphics.h b/minui/graphics.h index 94b89a554..993e986ee 100644 --- a/minui/graphics.h +++ b/minui/graphics.h @@ -41,6 +41,7 @@ typedef struct minui_backend { } minui_backend; minui_backend* open_fbdev(); +minui_backend* open_adf(); #ifdef __cplusplus } diff --git a/minui/graphics_adf.c b/minui/graphics_adf.c new file mode 100644 index 000000000..ac6d64e9e --- /dev/null +++ b/minui/graphics_adf.c @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2014 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 +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "graphics.h" + +struct adf_surface_pdata { + GRSurface base; + int fd; + __u32 offset; + __u32 pitch; +}; + +struct adf_pdata { + minui_backend base; + int intf_fd; + adf_id_t eng_id; + __u32 format; + + unsigned int current_surface; + unsigned int n_surfaces; + struct adf_surface_pdata surfaces[2]; +}; + +static gr_surface adf_flip(struct minui_backend *backend); +static void adf_blank(struct minui_backend *backend, bool blank); + +static int adf_surface_init(struct adf_pdata *pdata, + struct drm_mode_modeinfo *mode, struct adf_surface_pdata *surf) +{ + memset(surf, 0, sizeof(*surf)); + + surf->fd = adf_interface_simple_buffer_alloc(pdata->intf_fd, mode->hdisplay, + mode->vdisplay, pdata->format, &surf->offset, &surf->pitch); + if (surf->fd < 0) + return surf->fd; + + surf->base.width = mode->hdisplay; + surf->base.height = mode->vdisplay; + surf->base.row_bytes = surf->pitch; + surf->base.pixel_bytes = (pdata->format == DRM_FORMAT_RGB565) ? 2 : 4; + + surf->base.data = mmap(NULL, surf->pitch * surf->base.height, PROT_WRITE, + MAP_SHARED, surf->fd, surf->offset); + if (surf->base.data == MAP_FAILED) { + close(surf->fd); + return -errno; + } + + return 0; +} + +static int adf_interface_init(struct adf_pdata *pdata) +{ + struct adf_interface_data intf_data; + int ret = 0; + int err; + + err = adf_get_interface_data(pdata->intf_fd, &intf_data); + if (err < 0) + return err; + + err = adf_surface_init(pdata, &intf_data.current_mode, &pdata->surfaces[0]); + if (err < 0) { + fprintf(stderr, "allocating surface 0 failed: %s\n", strerror(-err)); + ret = err; + goto done; + } + + err = adf_surface_init(pdata, &intf_data.current_mode, + &pdata->surfaces[1]); + if (err < 0) { + fprintf(stderr, "allocating surface 1 failed: %s\n", strerror(-err)); + memset(&pdata->surfaces[1], 0, sizeof(pdata->surfaces[1])); + pdata->n_surfaces = 1; + } else { + pdata->n_surfaces = 2; + } + +done: + adf_free_interface_data(&intf_data); + return ret; +} + +static int adf_device_init(struct adf_pdata *pdata, struct adf_device *dev) +{ + adf_id_t intf_id; + int intf_fd; + int err; + + err = adf_find_simple_post_configuration(dev, &pdata->format, 1, &intf_id, + &pdata->eng_id); + if (err < 0) + return err; + + err = adf_device_attach(dev, pdata->eng_id, intf_id); + if (err < 0 && err != -EALREADY) + return err; + + pdata->intf_fd = adf_interface_open(dev, intf_id, O_RDWR); + if (pdata->intf_fd < 0) + return pdata->intf_fd; + + err = adf_interface_init(pdata); + if (err < 0) { + close(pdata->intf_fd); + pdata->intf_fd = -1; + } + + return err; +} + +static gr_surface adf_init(minui_backend *backend) +{ + struct adf_pdata *pdata = (struct adf_pdata *)backend; + adf_id_t *dev_ids = NULL; + ssize_t n_dev_ids, i; + gr_surface ret; + +#if defined(RECOVERY_BGRA) + pdata->format = DRM_FORMAT_BGRA8888; +#elif defined(RECOVERY_RGBX) + pdata->format = DRM_FORMAT_RGBX8888; +#else + pdata->format = DRM_FORMAT_RGB565; +#endif + + n_dev_ids = adf_devices(&dev_ids); + if (n_dev_ids == 0) { + return NULL; + } else if (n_dev_ids < 0) { + fprintf(stderr, "enumerating adf devices failed: %s\n", + strerror(-n_dev_ids)); + return NULL; + } + + pdata->intf_fd = -1; + + for (i = 0; i < n_dev_ids && pdata->intf_fd < 0; i++) { + struct adf_device dev; + + int err = adf_device_open(dev_ids[i], O_RDWR, &dev); + if (err < 0) { + fprintf(stderr, "opening adf device %u failed: %s\n", dev_ids[i], + strerror(-err)); + continue; + } + + err = adf_device_init(pdata, &dev); + if (err < 0) + fprintf(stderr, "initializing adf device %u failed: %s\n", + dev_ids[i], strerror(-err)); + + adf_device_close(&dev); + } + + free(dev_ids); + + if (pdata->intf_fd < 0) + return NULL; + + ret = adf_flip(backend); + + adf_blank(backend, true); + adf_blank(backend, false); + + return ret; +} + +static gr_surface adf_flip(struct minui_backend *backend) +{ + struct adf_pdata *pdata = (struct adf_pdata *)backend; + struct adf_surface_pdata *surf = &pdata->surfaces[pdata->current_surface]; + + int fence_fd = adf_interface_simple_post(pdata->intf_fd, pdata->eng_id, + surf->base.width, surf->base.height, pdata->format, surf->fd, + surf->offset, surf->pitch, -1); + if (fence_fd >= 0) + close(fence_fd); + + pdata->current_surface = (pdata->current_surface + 1) % pdata->n_surfaces; + return &pdata->surfaces[pdata->current_surface].base; +} + +static void adf_blank(struct minui_backend *backend, bool blank) +{ + struct adf_pdata *pdata = (struct adf_pdata *)backend; + adf_interface_blank(pdata->intf_fd, + blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON); +} + +static void adf_surface_destroy(struct adf_surface_pdata *surf) +{ + munmap(surf->base.data, surf->pitch * surf->base.height); + close(surf->fd); +} + +static void adf_exit(struct minui_backend *backend) +{ + struct adf_pdata *pdata = (struct adf_pdata *)backend; + unsigned int i; + + for (i = 0; i < pdata->n_surfaces; i++) + adf_surface_destroy(&pdata->surfaces[i]); + if (pdata->intf_fd >= 0) + close(pdata->intf_fd); + free(pdata); +} + +minui_backend *open_adf() +{ + struct adf_pdata *pdata = calloc(1, sizeof(*pdata)); + if (!pdata) { + perror("allocating adf backend failed"); + return NULL; + } + + pdata->base.init = adf_init; + pdata->base.flip = adf_flip; + pdata->base.blank = adf_blank; + pdata->base.exit = adf_exit; + return &pdata->base; +} -- cgit v1.2.3 From aa1a31e83d25e6c3c3371063704eeb14558a595e Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Fri, 25 Apr 2014 18:47:18 -0700 Subject: Allow lines without = signs. The new build.prop for Sprout includes lines of the format: import xxx.prop These can be safely ignored when reading the property file. Change-Id: Ia84a138e71461ffe8e591e88143b9787873def29 --- updater/install.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/updater/install.c b/updater/install.c index 53f5e48cb..8defc7720 100644 --- a/updater/install.c +++ b/updater/install.c @@ -927,8 +927,8 @@ Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) { // 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). +// per line. # comment lines,blank lines, lines without '=' ignored), +// and returns the value for 'key' (or "" if it isn't defined). Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; char* buffer = NULL; @@ -986,9 +986,7 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { char* equal = strchr(line, '='); if (equal == NULL) { - ErrorAbort(state, "%s: malformed line \"%s\": %s not a prop file?", - name, line, filename); - goto done; + continue; } // trim whitespace between key and '=' -- cgit v1.2.3 From f5d9f891524862ba560650bd545668dc22622cdb Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Tue, 6 May 2014 16:54:42 -0700 Subject: Allow 0-byte files in full OTAs. Currently, the writeProcessFunction fails when there are zero bytes to write, potentially returning errno from a previous operation, or hanging indefinitely while it waits for a >0 result on a write of size 0. This happens when the output file is intended to be zero bytes in size. Change-Id: Ib3cfcaf66d82942bc89e5f5c64697862403b38da --- minzip/Zip.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/minzip/Zip.c b/minzip/Zip.c index abc98901c..5070104d3 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -698,7 +698,9 @@ static bool writeProcessFunction(const unsigned char *data, int dataLen, void *cookie) { int fd = (int)(intptr_t)cookie; - + if (dataLen == 0) { + return true; + } ssize_t soFar = 0; while (true) { ssize_t n = write(fd, data+soFar, dataLen-soFar); -- cgit v1.2.3 From 4aa12dd0decafb139239779ab38e6ffda23109ab Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 13 May 2014 08:40:49 -0700 Subject: fix vulnerability in bspatch Patches with control data tuples with negative numbers in the first and/or second can cause bspatch to write to arbitrary locations in the heap. Change-Id: I8c5d81948be773e6483241131d3d166b6da27cb8 --- applypatch/bspatch.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/applypatch/bspatch.c b/applypatch/bspatch.c index 2e80f81d0..1dc7ab10b 100644 --- a/applypatch/bspatch.c +++ b/applypatch/bspatch.c @@ -205,6 +205,11 @@ int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, ctrl[1] = offtin(buf+8); ctrl[2] = offtin(buf+16); + if (ctrl[0] < 0 || ctrl[1] < 0) { + printf("corrupt patch (negative byte counts)\n"); + return 1; + } + // Sanity check if (newpos + ctrl[0] > *new_size) { printf("corrupt patch (new file overrun)\n"); -- cgit v1.2.3 From f766396d9931e1879161132c26fe61453065f450 Mon Sep 17 00:00:00 2001 From: Daniel Leung Date: Wed, 19 Mar 2014 13:41:31 -0700 Subject: Support BGRA framebuffer The re-designed recovery graphics code only supports RGB{_,X,A} framebuffer, and this patch adds support for BGRA framebuffer. Change-Id: I3780c8288088f497fa248f3492c54f43834a8598 Signed-off-by: Daniel Leung Reviewed-by: Jovanovic, Radivoje Reviewed-by: Parkinson, TimothyX L Tested-by: Parkinson, TimothyX L Reviewed-by: Boie, Andrew P Reviewed-by: Gumbel, Matthew K --- minui/graphics_fbdev.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/minui/graphics_fbdev.c b/minui/graphics_fbdev.c index bb91975a9..a91ea8726 100644 --- a/minui/graphics_fbdev.c +++ b/minui/graphics_fbdev.c @@ -184,8 +184,21 @@ static gr_surface fbdev_flip(minui_backend* backend __unused) { set_displayed_framebuffer(1-displayed_buffer); } else { // Copy from the in-memory surface to the framebuffer. + +#if defined(RECOVERY_BGRA) + unsigned int idx; + unsigned char* ucfb_vaddr = (unsigned char*)gr_framebuffer[0].data; + unsigned char* ucbuffer_vaddr = (unsigned char*)gr_draw->data; + for (idx = 0 ; idx < (gr_draw->height * gr_draw->row_bytes); idx += 4) { + ucfb_vaddr[idx ] = ucbuffer_vaddr[idx + 2]; + ucfb_vaddr[idx + 1] = ucbuffer_vaddr[idx + 1]; + ucfb_vaddr[idx + 2] = ucbuffer_vaddr[idx ]; + ucfb_vaddr[idx + 3] = ucbuffer_vaddr[idx + 3]; + } +#else memcpy(gr_framebuffer[0].data, gr_draw->data, gr_draw->height * gr_draw->row_bytes); +#endif } return gr_draw; } -- cgit v1.2.3 From c704e06ce596bd0a6de66b10b108aee95535468a Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 23 May 2014 08:40:35 -0700 Subject: disable async reboot during package installation The default recovery UI will reboot the device when the power key is pressed 7 times in a row, regardless of what recovery is doing. Disable this feature during package installation, to minimize the chance of corrupting the device due to a mid-install reboot. (Debug packages can explicitly request that the feature be reenabled.) Change-Id: I20f3ec240ecd344615d452005ff26d8dd7775acf --- install.cpp | 7 +++++++ ui.cpp | 25 +++++++++++++++++++++---- ui.h | 8 ++++++++ updater/install.c | 11 +++++++++++ 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/install.cpp b/install.cpp index 0bd7945c4..0f059602a 100644 --- a/install.cpp +++ b/install.cpp @@ -160,6 +160,11 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { *wipe_cache = 1; } else if (strcmp(command, "clear_display") == 0) { ui->SetBackground(RecoveryUI::NONE); + } else if (strcmp(command, "enable_reboot") == 0) { + // packages can explicitly request that they want the user + // to be able to reboot during installation (useful for + // debugging packages that don't exit). + ui->SetEnableReboot(true); } else { LOGE("unknown command [%s]\n", command); } @@ -236,7 +241,9 @@ really_install_package(const char *path, int* wipe_cache) /* Verify and install the contents of the package. */ ui->Print("Installing update...\n"); + ui->SetEnableReboot(false); int result = try_update_binary(path, &zip, wipe_cache); + ui->SetEnableReboot(true); sysReleaseMap(&map); diff --git a/ui.cpp b/ui.cpp index 091012f29..98442539b 100644 --- a/ui.cpp +++ b/ui.cpp @@ -49,6 +49,7 @@ RecoveryUI::RecoveryUI() : key_last_down(-1), key_long_press(false), key_down_count(0), + enable_reboot(true), consecutive_power_keys(0), consecutive_alternate_keys(0), last_key(-1) { @@ -116,6 +117,7 @@ int RecoveryUI::input_callback(int fd, uint32_t epevents, void* data) void RecoveryUI::process_key(int key_code, int updown) { bool register_key = false; bool long_press = false; + bool reboot_enabled; pthread_mutex_lock(&key_queue_mutex); key_pressed[key_code] = updown; @@ -137,6 +139,7 @@ void RecoveryUI::process_key(int key_code, int updown) { } key_last_down = -1; } + reboot_enabled = enable_reboot; pthread_mutex_unlock(&key_queue_mutex); if (register_key) { @@ -150,7 +153,9 @@ void RecoveryUI::process_key(int key_code, int updown) { break; case RecoveryUI::REBOOT: - android_reboot(ANDROID_RB_RESTART, 0, 0); + if (reboot_enabled) { + android_reboot(ANDROID_RB_RESTART, 0, 0); + } break; case RecoveryUI::ENQUEUE: @@ -281,9 +286,15 @@ RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) { } if (key == KEY_POWER) { - ++consecutive_power_keys; - if (consecutive_power_keys >= 7) { - return REBOOT; + pthread_mutex_lock(&key_queue_mutex); + bool reboot_enabled = enable_reboot; + pthread_mutex_unlock(&key_queue_mutex); + + if (reboot_enabled) { + ++consecutive_power_keys; + if (consecutive_power_keys >= 7) { + return REBOOT; + } } } else { consecutive_power_keys = 0; @@ -311,3 +322,9 @@ void RecoveryUI::NextCheckKeyIsLong(bool is_long_press) { void RecoveryUI::KeyLongPress(int key) { } + +void RecoveryUI::SetEnableReboot(bool enabled) { + pthread_mutex_lock(&key_queue_mutex); + enable_reboot = enabled; + pthread_mutex_unlock(&key_queue_mutex); +} diff --git a/ui.h b/ui.h index faa0acd46..31a8a7fb1 100644 --- a/ui.h +++ b/ui.h @@ -93,6 +93,13 @@ class RecoveryUI { // be called with "true". virtual void KeyLongPress(int key); + // Normally in recovery there's a key sequence that triggers + // immediate reboot of the device, regardless of what recovery is + // doing (with the default CheckKey implementation, it's pressing + // the power button 7 times in row). Call this to enable or + // disable that feature. It is enabled by default. + virtual void SetEnableReboot(bool enabled); + // --- menu display --- // Display some header text followed by a menu of items, which appears @@ -121,6 +128,7 @@ private: int key_last_down; // under key_queue_mutex bool key_long_press; // under key_queue_mutex int key_down_count; // under key_queue_mutex + bool enable_reboot; // under key_queue_mutex int rel_sum; int consecutive_power_keys; diff --git a/updater/install.c b/updater/install.c index 8defc7720..e1071c90c 100644 --- a/updater/install.c +++ b/updater/install.c @@ -1640,6 +1640,15 @@ Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) return StringValue(strdup(success ? "t" : "")); } +Value* EnableRebootFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 0) { + return ErrorAbort(state, "%s() expects no args, got %d", name, argc); + } + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); + fprintf(ui->cmd_pipe, "enable_reboot\n"); + return StringValue(strdup("t")); +} + void RegisterInstallFunctions() { RegisterFunction("mount", MountFn); RegisterFunction("is_mounted", IsMountedFn); @@ -1689,4 +1698,6 @@ void RegisterInstallFunctions() { RegisterFunction("reboot_now", RebootNowFn); RegisterFunction("get_stage", GetStageFn); RegisterFunction("set_stage", SetStageFn); + + RegisterFunction("enable_reboot", EnableRebootFn); } -- cgit v1.2.3 From dff8afe1b21c6ffe09269434f9f8f0d166e1a848 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 3 Jun 2014 10:50:13 -0700 Subject: restore holo UI in recovery -- DO NOT MERGE Since we don't have quantum assets for recovery yet, go back to the holo appearance (dark background, blue glowing progress bar) for lmp-preview. Change-Id: Id4d3f23e0a6251a12aa42f3793cff347f38b4243 --- res-hdpi/images/progress_empty.png | Bin 81 -> 118 bytes res-hdpi/images/progress_fill.png | Bin 83 -> 404 bytes res-mdpi/images/progress_empty.png | Bin 74 -> 118 bytes res-mdpi/images/progress_fill.png | Bin 83 -> 404 bytes res-xhdpi/images/progress_empty.png | Bin 84 -> 118 bytes res-xhdpi/images/progress_fill.png | Bin 84 -> 404 bytes res-xxhdpi/images/progress_empty.png | Bin 86 -> 118 bytes res-xxhdpi/images/progress_fill.png | Bin 86 -> 404 bytes res-xxxhdpi/images/progress_empty.png | Bin 90 -> 118 bytes res-xxxhdpi/images/progress_fill.png | Bin 90 -> 404 bytes screen_ui.cpp | 29 ++++++++++++++++------------- screen_ui.h | 3 --- 12 files changed, 16 insertions(+), 16 deletions(-) diff --git a/res-hdpi/images/progress_empty.png b/res-hdpi/images/progress_empty.png index b29d9be0c..72581832c 100644 Binary files a/res-hdpi/images/progress_empty.png and b/res-hdpi/images/progress_empty.png differ diff --git a/res-hdpi/images/progress_fill.png b/res-hdpi/images/progress_fill.png index 4c5e66bb5..becf87bdf 100644 Binary files a/res-hdpi/images/progress_fill.png and b/res-hdpi/images/progress_fill.png differ diff --git a/res-mdpi/images/progress_empty.png b/res-mdpi/images/progress_empty.png index 0883e0070..72581832c 100644 Binary files a/res-mdpi/images/progress_empty.png and b/res-mdpi/images/progress_empty.png differ diff --git a/res-mdpi/images/progress_fill.png b/res-mdpi/images/progress_fill.png index c223871b9..becf87bdf 100644 Binary files a/res-mdpi/images/progress_fill.png and b/res-mdpi/images/progress_fill.png differ diff --git a/res-xhdpi/images/progress_empty.png b/res-xhdpi/images/progress_empty.png index 429797bf0..72581832c 100644 Binary files a/res-xhdpi/images/progress_empty.png and b/res-xhdpi/images/progress_empty.png differ diff --git a/res-xhdpi/images/progress_fill.png b/res-xhdpi/images/progress_fill.png index 7af35aaf1..becf87bdf 100644 Binary files a/res-xhdpi/images/progress_fill.png and b/res-xhdpi/images/progress_fill.png differ diff --git a/res-xxhdpi/images/progress_empty.png b/res-xxhdpi/images/progress_empty.png index 57c823587..72581832c 100644 Binary files a/res-xxhdpi/images/progress_empty.png and b/res-xxhdpi/images/progress_empty.png differ diff --git a/res-xxhdpi/images/progress_fill.png b/res-xxhdpi/images/progress_fill.png index 0f36f48c2..becf87bdf 100644 Binary files a/res-xxhdpi/images/progress_fill.png and b/res-xxhdpi/images/progress_fill.png differ diff --git a/res-xxxhdpi/images/progress_empty.png b/res-xxxhdpi/images/progress_empty.png index ba25afb6e..72581832c 100644 Binary files a/res-xxxhdpi/images/progress_empty.png and b/res-xxxhdpi/images/progress_empty.png differ diff --git a/res-xxxhdpi/images/progress_fill.png b/res-xxxhdpi/images/progress_fill.png index 83b72b27e..becf87bdf 100644 Binary files a/res-xxxhdpi/images/progress_fill.png and b/res-xxxhdpi/images/progress_fill.png differ diff --git a/screen_ui.cpp b/screen_ui.cpp index d087af329..03ef049ae 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -89,7 +89,7 @@ ScreenRecoveryUI::ScreenRecoveryUI() : void ScreenRecoveryUI::draw_background_locked(Icon icon) { pagesIdentical = false; - gr_color(250, 250, 250, 255); + gr_color(0, 0, 0, 255); gr_clear(); if (icon) { @@ -125,7 +125,7 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) } } - gr_color(115, 115, 115, 255); + gr_color(255, 255, 255, 255); gr_texticon(textX, textY, text_surface); } } @@ -150,7 +150,7 @@ void ScreenRecoveryUI::draw_progress_locked() int dy = (3*gr_fb_height() + iconHeight - 2*height)/4; // Erase behind the progress bar (in case this was a progress-only update) - gr_color(250, 250, 250, 255); + gr_color(0, 0, 0, 255); gr_fill(dx, dy, width, height); if (progressBarType == DETERMINATE) { @@ -181,17 +181,20 @@ void ScreenRecoveryUI::draw_progress_locked() void ScreenRecoveryUI::SetColor(UIElement e) { switch (e) { case HEADER: - gr_color(0xff, 0x57, 0x22, 255); // Quantum "Deep Orange" 500 + gr_color(247, 0, 6, 255); break; case MENU: case MENU_SEL_BG: - gr_color(0x67, 0x3a, 0xb7, 255); // Quantum "Deep Purple" 500 + gr_color(0, 106, 157, 255); break; case MENU_SEL_FG: gr_color(255, 255, 255, 255); break; case LOG: - gr_color(0x3f, 0x51, 0xb5, 255); // Quantum "Indigo" 500 + gr_color(249, 194, 0, 255); + break; + case TEXT_FILL: + gr_color(0, 0, 0, 160); break; default: gr_color(255, 255, 255, 255); @@ -207,7 +210,7 @@ void ScreenRecoveryUI::draw_screen_locked() draw_background_locked(currentIcon); draw_progress_locked(); } else { - gr_color(250, 250, 250, 255); + gr_color(0, 0, 0, 255); gr_clear(); int y = 0; @@ -221,13 +224,13 @@ void ScreenRecoveryUI::draw_screen_locked() if (i == menu_top + menu_sel) { // draw the highlight bar SetColor(MENU_SEL_BG); - gr_fill(0, y-2+kTextYOffset, gr_fb_width(), y+char_height+2+kTextYOffset); + gr_fill(0, y-2, gr_fb_width(), y+char_height+2); // white text of selected item SetColor(MENU_SEL_FG); - if (menu[i][0]) gr_text(kTextXOffset, y+kTextYOffset, menu[i], 1); + if (menu[i][0]) gr_text(4, y, menu[i], 1); SetColor(MENU); } else { - if (menu[i][0]) gr_text(kTextXOffset, y+kTextYOffset, menu[i], i < menu_top); + if (menu[i][0]) gr_text(4, y, menu[i], i < menu_top); } y += char_height+4; } @@ -248,7 +251,7 @@ void ScreenRecoveryUI::draw_screen_locked() for (int ty = gr_fb_height() - char_height, count = 0; ty > y+2 && count < text_rows; ty -= char_height, ++count) { - gr_text(kTextXOffset, ty+kTextYOffset, text[row], 0); + gr_text(4, ty, text[row], 0); --row; if (row < 0) row = text_rows-1; } @@ -349,11 +352,11 @@ void ScreenRecoveryUI::Init() gr_font_size(&char_width, &char_height); text_col = text_row = 0; - text_rows = (gr_fb_height() - 2 * kTextYOffset) / char_height; + text_rows = gr_fb_height() / char_height; if (text_rows > kMaxRows) text_rows = kMaxRows; text_top = 1; - text_cols = (gr_fb_width() - 2 * kTextXOffset) / char_width; + text_cols = gr_fb_width() / char_width; if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1; backgroundIcon[NONE] = NULL; diff --git a/screen_ui.h b/screen_ui.h index d5e589225..01a33bfe2 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -87,9 +87,6 @@ class ScreenRecoveryUI : public RecoveryUI { static const int kMaxCols = 96; static const int kMaxRows = 96; - static const int kTextXOffset = 8; - static const int kTextYOffset = 4; - // Log text overlay, displayed when a magic key is pressed char text[kMaxRows][kMaxCols]; int text_cols, text_rows; -- cgit v1.2.3 From 187d626e6108298b436f22657a8e14658c15d910 Mon Sep 17 00:00:00 2001 From: Mihai Serban Date: Fri, 6 Jun 2014 15:23:20 +0300 Subject: recovery: initialize keys press tracking status Checks for keys pressed return random results because of an uninitialized data structure. Change-Id: Ic8b3d453d62347921aa893403079b374c16a092e Signed-off-by: Mihai Serban --- ui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ui.cpp b/ui.cpp index 5043ee528..a7c8bea48 100644 --- a/ui.cpp +++ b/ui.cpp @@ -55,6 +55,7 @@ RecoveryUI::RecoveryUI() : pthread_mutex_init(&key_queue_mutex, NULL); pthread_cond_init(&key_queue_cond, NULL); self = this; + memset(key_pressed, 0, sizeof(key_pressed)); } void RecoveryUI::Init() { -- cgit v1.2.3 From 5b5f6c2fd32839227d10ee3c97e662a415b80e2b Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 3 Jun 2014 10:50:13 -0700 Subject: restore holo UI in recovery Return to the recovery to the holo appearance. Bug: 15424396 Change-Id: Id4d3f23e0a6251a12aa42f3793cff347f38b4243 --- res-hdpi/images/progress_empty.png | Bin 81 -> 118 bytes res-hdpi/images/progress_fill.png | Bin 83 -> 404 bytes res-mdpi/images/progress_empty.png | Bin 74 -> 118 bytes res-mdpi/images/progress_fill.png | Bin 83 -> 404 bytes res-xhdpi/images/progress_empty.png | Bin 84 -> 118 bytes res-xhdpi/images/progress_fill.png | Bin 84 -> 404 bytes res-xxhdpi/images/progress_empty.png | Bin 86 -> 118 bytes res-xxhdpi/images/progress_fill.png | Bin 86 -> 404 bytes res-xxxhdpi/images/progress_empty.png | Bin 90 -> 118 bytes res-xxxhdpi/images/progress_fill.png | Bin 90 -> 404 bytes screen_ui.cpp | 29 ++++++++++++++++------------- screen_ui.h | 3 --- 12 files changed, 16 insertions(+), 16 deletions(-) diff --git a/res-hdpi/images/progress_empty.png b/res-hdpi/images/progress_empty.png index b29d9be0c..72581832c 100644 Binary files a/res-hdpi/images/progress_empty.png and b/res-hdpi/images/progress_empty.png differ diff --git a/res-hdpi/images/progress_fill.png b/res-hdpi/images/progress_fill.png index 4c5e66bb5..becf87bdf 100644 Binary files a/res-hdpi/images/progress_fill.png and b/res-hdpi/images/progress_fill.png differ diff --git a/res-mdpi/images/progress_empty.png b/res-mdpi/images/progress_empty.png index 0883e0070..72581832c 100644 Binary files a/res-mdpi/images/progress_empty.png and b/res-mdpi/images/progress_empty.png differ diff --git a/res-mdpi/images/progress_fill.png b/res-mdpi/images/progress_fill.png index c223871b9..becf87bdf 100644 Binary files a/res-mdpi/images/progress_fill.png and b/res-mdpi/images/progress_fill.png differ diff --git a/res-xhdpi/images/progress_empty.png b/res-xhdpi/images/progress_empty.png index 429797bf0..72581832c 100644 Binary files a/res-xhdpi/images/progress_empty.png and b/res-xhdpi/images/progress_empty.png differ diff --git a/res-xhdpi/images/progress_fill.png b/res-xhdpi/images/progress_fill.png index 7af35aaf1..becf87bdf 100644 Binary files a/res-xhdpi/images/progress_fill.png and b/res-xhdpi/images/progress_fill.png differ diff --git a/res-xxhdpi/images/progress_empty.png b/res-xxhdpi/images/progress_empty.png index 57c823587..72581832c 100644 Binary files a/res-xxhdpi/images/progress_empty.png and b/res-xxhdpi/images/progress_empty.png differ diff --git a/res-xxhdpi/images/progress_fill.png b/res-xxhdpi/images/progress_fill.png index 0f36f48c2..becf87bdf 100644 Binary files a/res-xxhdpi/images/progress_fill.png and b/res-xxhdpi/images/progress_fill.png differ diff --git a/res-xxxhdpi/images/progress_empty.png b/res-xxxhdpi/images/progress_empty.png index ba25afb6e..72581832c 100644 Binary files a/res-xxxhdpi/images/progress_empty.png and b/res-xxxhdpi/images/progress_empty.png differ diff --git a/res-xxxhdpi/images/progress_fill.png b/res-xxxhdpi/images/progress_fill.png index 83b72b27e..becf87bdf 100644 Binary files a/res-xxxhdpi/images/progress_fill.png and b/res-xxxhdpi/images/progress_fill.png differ diff --git a/screen_ui.cpp b/screen_ui.cpp index d087af329..03ef049ae 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -89,7 +89,7 @@ ScreenRecoveryUI::ScreenRecoveryUI() : void ScreenRecoveryUI::draw_background_locked(Icon icon) { pagesIdentical = false; - gr_color(250, 250, 250, 255); + gr_color(0, 0, 0, 255); gr_clear(); if (icon) { @@ -125,7 +125,7 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) } } - gr_color(115, 115, 115, 255); + gr_color(255, 255, 255, 255); gr_texticon(textX, textY, text_surface); } } @@ -150,7 +150,7 @@ void ScreenRecoveryUI::draw_progress_locked() int dy = (3*gr_fb_height() + iconHeight - 2*height)/4; // Erase behind the progress bar (in case this was a progress-only update) - gr_color(250, 250, 250, 255); + gr_color(0, 0, 0, 255); gr_fill(dx, dy, width, height); if (progressBarType == DETERMINATE) { @@ -181,17 +181,20 @@ void ScreenRecoveryUI::draw_progress_locked() void ScreenRecoveryUI::SetColor(UIElement e) { switch (e) { case HEADER: - gr_color(0xff, 0x57, 0x22, 255); // Quantum "Deep Orange" 500 + gr_color(247, 0, 6, 255); break; case MENU: case MENU_SEL_BG: - gr_color(0x67, 0x3a, 0xb7, 255); // Quantum "Deep Purple" 500 + gr_color(0, 106, 157, 255); break; case MENU_SEL_FG: gr_color(255, 255, 255, 255); break; case LOG: - gr_color(0x3f, 0x51, 0xb5, 255); // Quantum "Indigo" 500 + gr_color(249, 194, 0, 255); + break; + case TEXT_FILL: + gr_color(0, 0, 0, 160); break; default: gr_color(255, 255, 255, 255); @@ -207,7 +210,7 @@ void ScreenRecoveryUI::draw_screen_locked() draw_background_locked(currentIcon); draw_progress_locked(); } else { - gr_color(250, 250, 250, 255); + gr_color(0, 0, 0, 255); gr_clear(); int y = 0; @@ -221,13 +224,13 @@ void ScreenRecoveryUI::draw_screen_locked() if (i == menu_top + menu_sel) { // draw the highlight bar SetColor(MENU_SEL_BG); - gr_fill(0, y-2+kTextYOffset, gr_fb_width(), y+char_height+2+kTextYOffset); + gr_fill(0, y-2, gr_fb_width(), y+char_height+2); // white text of selected item SetColor(MENU_SEL_FG); - if (menu[i][0]) gr_text(kTextXOffset, y+kTextYOffset, menu[i], 1); + if (menu[i][0]) gr_text(4, y, menu[i], 1); SetColor(MENU); } else { - if (menu[i][0]) gr_text(kTextXOffset, y+kTextYOffset, menu[i], i < menu_top); + if (menu[i][0]) gr_text(4, y, menu[i], i < menu_top); } y += char_height+4; } @@ -248,7 +251,7 @@ void ScreenRecoveryUI::draw_screen_locked() for (int ty = gr_fb_height() - char_height, count = 0; ty > y+2 && count < text_rows; ty -= char_height, ++count) { - gr_text(kTextXOffset, ty+kTextYOffset, text[row], 0); + gr_text(4, ty, text[row], 0); --row; if (row < 0) row = text_rows-1; } @@ -349,11 +352,11 @@ void ScreenRecoveryUI::Init() gr_font_size(&char_width, &char_height); text_col = text_row = 0; - text_rows = (gr_fb_height() - 2 * kTextYOffset) / char_height; + text_rows = gr_fb_height() / char_height; if (text_rows > kMaxRows) text_rows = kMaxRows; text_top = 1; - text_cols = (gr_fb_width() - 2 * kTextXOffset) / char_width; + text_cols = gr_fb_width() / char_width; if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1; backgroundIcon[NONE] = NULL; diff --git a/screen_ui.h b/screen_ui.h index d5e589225..01a33bfe2 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -87,9 +87,6 @@ class ScreenRecoveryUI : public RecoveryUI { static const int kMaxCols = 96; static const int kMaxRows = 96; - static const int kTextXOffset = 8; - static const int kTextYOffset = 4; - // Log text overlay, displayed when a magic key is pressed char text[kMaxRows][kMaxCols]; int text_cols, text_rows; -- cgit v1.2.3 From 43772d26a5d8d31fd092a21edfca346f3b3901e7 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 9 Jun 2014 14:13:19 -0700 Subject: advance progress bar during block OTA installations While executing syspatch and package_extract_file() calls with don't care maps (both of which are used to rewrite the system image in incremental and full block OTAs, respectively), pass a progress callback in and use it to update the visible progress bar. Change-Id: I1d3742d167c1bb2130571eb5103b7795c65ff371 --- updater/install.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/updater/install.c b/updater/install.c index e1071c90c..42f228972 100644 --- a/updater/install.c +++ b/updater/install.c @@ -463,17 +463,31 @@ DontCareMap* ReadDontCareMapFromZip(ZipArchive* za, const char* path) { map->region_count = strtoul(p, &p, 0); map->regions = (int*) malloc(map->region_count * sizeof(int)); + map->total_blocks = 0; int i; for (i = 0; i < map->region_count; ++i) { map->regions[i] = strtoul(p, &p, 0); + map->total_blocks += map->regions[i]; } return map; } +static FILE* mapwrite_cmd_pipe; + +static void progress_cb(long done, long total) { + if (total > 0) { + double frac = (double)done / total; + fprintf(mapwrite_cmd_pipe, "set_progress %f\n", frac); + fflush(mapwrite_cmd_pipe); + } +} + + + bool MapWriter(const unsigned char* data, int dataLen, void* cookie) { - return write_with_map(data, dataLen, (MapState*) cookie) == dataLen; + return write_with_map(data, dataLen, (MapState*) cookie, progress_cb) == dataLen; } // package_extract_file(package_path, destination_path, map_path) @@ -490,6 +504,10 @@ Value* PackageExtractFileFn(const char* name, State* state, name, argc); } bool success = false; + + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); + mapwrite_cmd_pipe = ui->cmd_pipe; + if (argc >= 2) { // The two-argument version extracts to a file; the three-arg // version extracts to a file, skipping over regions in a @@ -1185,6 +1203,9 @@ Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) { return NULL; } + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); + mapwrite_cmd_pipe = ui->cmd_pipe; + if (ParseSha1(target_sha1, target_digest) != 0) { printf("%s(): failed to parse '%s' as target SHA-1", name, target_sha1); memset(target_digest, 0, SHA_DIGEST_SIZE); @@ -1220,7 +1241,7 @@ Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) { FILE* tgt = fopen(filename, "r+"); - int ret = syspatch(src, init_map, patch_data, patch_len, tgt, target_map); + int ret = syspatch(src, init_map, patch_data, patch_len, tgt, target_map, progress_cb); fclose(src); fclose(tgt); -- cgit v1.2.3 From ba545d7e2388e81e985a43fc86d191590b923c90 Mon Sep 17 00:00:00 2001 From: Joey Pan Date: Wed, 4 Jun 2014 20:47:46 +0800 Subject: minui: avoid random screen frame at startup Clear framebuffer at init. [toddpoynor@google.com: forward port] Change-Id: Ie98c7724cd974dcacef3e3559a6fe492864a5e72 --- minui/graphics_fbdev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/minui/graphics_fbdev.c b/minui/graphics_fbdev.c index a91ea8726..c0c1bcb1a 100644 --- a/minui/graphics_fbdev.c +++ b/minui/graphics_fbdev.c @@ -130,6 +130,8 @@ static gr_surface fbdev_init(minui_backend* backend) { return NULL; } + memset(bits, 0, fi.smem_len); + gr_framebuffer[0].width = vi.xres; gr_framebuffer[0].height = vi.yres; gr_framebuffer[0].row_bytes = fi.line_length; -- cgit v1.2.3 From 37aedb3fafcccd0da5bd9089987f05895c27492d Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 16 Jun 2014 19:07:39 -0700 Subject: Support F2FS for the data partition This adds F2FS support - for wiping a device - for the install "format" command. Note: crypto data in "footer" with a default/negative length is not supported, unlike with "ext4". Change-Id: I8d141a0d4d14df9fe84d3b131484e9696fcd8870 Signed-off-by: JP Abgrall --- Android.mk | 6 ++++++ roots.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++------------ updater/install.c | 37 +++++++++++++++++++++++++++++++-- 3 files changed, 90 insertions(+), 15 deletions(-) diff --git a/Android.mk b/Android.mk index 577679c06..1970956b5 100644 --- a/Android.mk +++ b/Android.mk @@ -32,6 +32,12 @@ LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true +# Not working as expected? +# LOCAL_ADDITIONAL_DEPENDENCIES := mkfs.f2fs +# LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_RECOVERY_ROOT_OUT)/sbin/mkfs.f2fs +LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_ROOT_OUT_SBIN)/mkfs.f2fs + + RECOVERY_API_VERSION := 3 RECOVERY_FSTAB_VERSION := 2 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) diff --git a/roots.cpp b/roots.cpp index 66481a3b9..8f9901908 100644 --- a/roots.cpp +++ b/roots.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -151,6 +152,20 @@ int ensure_path_unmounted(const char* path) { return unmount_mounted_volume(mv); } +static int exec_cmd(const char* path, char* const argv[]) { + int status; + pid_t child; + if ((child = vfork()) == 0) { + execv(path, argv); + _exit(-1); + } + waitpid(child, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + LOGE("%s failed with status %d\n", path, WEXITSTATUS(status)); + } + return WEXITSTATUS(status); +} + int format_volume(const char* volume) { Volume* v = volume_for_path(volume); if (v == NULL) { @@ -195,19 +210,7 @@ int format_volume(const char* volume) { return 0; } - if (strcmp(v->fs_type, "ext4") == 0) { - ssize_t length = 0; - if (v->length != 0) { - length = v->length; - } else if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0) { - length = -CRYPT_FOOTER_OFFSET; - } - int result = make_ext4fs(v->blk_device, length, volume, sehandle); - if (result != 0) { - LOGE("format_volume: make_extf4fs failed on %s\n", v->blk_device); - return -1; - } - + if (strcmp(v->fs_type, "ext4") == 0 || strcmp(v->fs_type, "f2fs") == 0) { // if there's a key_loc that looks like a path, it should be a // block device for storing encryption metadata. wipe it too. if (v->key_loc != NULL && v->key_loc[0] == '/') { @@ -221,6 +224,39 @@ int format_volume(const char* volume) { close(fd); } + ssize_t length = 0; + if (v->length != 0) { + length = v->length; + } else if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0) { + length = -CRYPT_FOOTER_OFFSET; + } + int result; + if (strcmp(v->fs_type, "ext4") == 0) { + result = make_ext4fs(v->blk_device, length, volume, sehandle); + } else { /* Has to be f2fs because we checked earlier. */ + if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) { + LOGE("format_volume: crypt footer + negative length (%lld) not supported on %s\n", v->fs_type, length); + return -1; + } + if (length < 0) { + LOGE("format_volume: negative length (%ld) not supported on %s\n", length, v->fs_type); + return -1; + } + char *num_sectors; + if (asprintf(&num_sectors, "%ld", length / 512) <= 0) { + LOGE("format_volume: failed to create %s command for %s\n", v->fs_type, v->blk_device); + return -1; + } + const char *f2fs_path = "/sbin/mkfs.f2fs"; + const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", v->blk_device, num_sectors, NULL}; + + result = exec_cmd(f2fs_path, (char* const*)f2fs_argv); + free(num_sectors); + } + if (result != 0) { + LOGE("format_volume: make %s failed on %s with %d(%s)\n", v->fs_type, v->blk_device, result, strerror(errno)); + return -1; + } return 0; } diff --git a/updater/install.c b/updater/install.c index 42f228972..edc386dc6 100644 --- a/updater/install.c +++ b/updater/install.c @@ -211,14 +211,29 @@ done: return StringValue(result); } +static int exec_cmd(const char* path, char* const argv[]) { + int status; + pid_t child; + if ((child = vfork()) == 0) { + execv(path, argv); + _exit(-1); + } + waitpid(child, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + printf("%s failed with status %d\n", path, WEXITSTATUS(status)); + } + return WEXITSTATUS(status); +} + // format(fs_type, partition_type, location, fs_size, mount_point) // // fs_type="yaffs2" partition_type="MTD" location=partition fs_size= mount_point= // fs_type="ext4" partition_type="EMMC" location=device fs_size= mount_point= -// if fs_size == 0, then make_ext4fs uses the entire partition. +// fs_type="f2fs" partition_type="EMMC" location=device fs_size= mount_point= +// if fs_size == 0, then make fs uses the entire partition. // if fs_size > 0, that is the size to use -// if fs_size < 0, then reserve that many bytes at the end of the partition +// if fs_size < 0, then reserve that many bytes at the end of the partition (not for "f2fs") Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 5) { @@ -290,6 +305,24 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { goto done; } result = location; + } else if (strcmp(fs_type, "f2fs") == 0) { + char *num_sectors; + if (asprintf(&num_sectors, "%lld", atoll(fs_size) / 512) <= 0) { + printf("format_volume: failed to create %s command for %s\n", fs_type, location); + result = strdup(""); + goto done; + } + const char *f2fs_path = "/sbin/mkfs.f2fs"; + const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", location, num_sectors, NULL}; + int status = exec_cmd(f2fs_path, (char* const*)f2fs_argv); + free(num_sectors); + if (status != 0) { + printf("%s: mkfs.f2fs failed (%d) on %s", + name, status, location); + result = strdup(""); + goto done; + } + result = location; #endif } else { printf("%s: unsupported fs_type \"%s\" partition_type \"%s\"", -- cgit v1.2.3 From b9afc2bf15822d09af188912ac27bd81bf078394 Mon Sep 17 00:00:00 2001 From: Ji-Hwan Lee Date: Thu, 19 Jun 2014 02:02:50 +0900 Subject: Use LOCAL_REQUIRED_MODULES for mkfs.f2fs Instead of LOCAL_ADDITIONAL_DEPENDENCIES. Bug: 15702524 Change-Id: Ic152ae60354bf09eccdb9a85dcd04f0f076a6422 --- Android.mk | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Android.mk b/Android.mk index 1970956b5..1165acba4 100644 --- a/Android.mk +++ b/Android.mk @@ -32,11 +32,9 @@ LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true -# Not working as expected? -# LOCAL_ADDITIONAL_DEPENDENCIES := mkfs.f2fs -# LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_RECOVERY_ROOT_OUT)/sbin/mkfs.f2fs -LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_ROOT_OUT_SBIN)/mkfs.f2fs - +ifeq ($(HOST_OS),linux) +LOCAL_REQUIRED_MODULES := mkfs.f2fs +endif RECOVERY_API_VERSION := 3 RECOVERY_FSTAB_VERSION := 2 -- cgit v1.2.3 From e739d7e6cd94e8300f9ea61a2d57d88328755b9f Mon Sep 17 00:00:00 2001 From: Riley Andrews Date: Tue, 24 Jun 2014 16:29:16 -0700 Subject: Fix recovery mode. Duplicate changes made to init.rc for https://android-review.googlesource.com/98852 in the init.rc used for recovery mode. Bug 15849856 Change-Id: Ia376ddf6373a28718653f7fb1435bf7ecb33d813 --- etc/init.rc | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/etc/init.rc b/etc/init.rc index 6e0595b44..cd25d98dd 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -44,13 +44,31 @@ on fs on boot - ifup lo hostname localhost domainname localdomain class_start default +# Load properties from /system/ + /factory after fs mount. +on load_all_props_action + load_all_props + +# Mount filesystems and start core system services. +on late-init + trigger early-fs + trigger fs + trigger post-fs + trigger post-fs-data + + # Load properties from /system/ + /factory after fs mount. Place + # this in another action so that the load will be scheduled after the prior + # issued fs triggers have completed. + trigger load_all_props_action + + trigger early-boot + trigger boot + on property:sys.powerctl=* powerctl ${sys.powerctl} -- cgit v1.2.3 From 22bcf97a5949816db85242157cb2640feed73616 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 24 Jun 2014 13:43:39 -0700 Subject: recovery: enable panic_on_oops Set panic_on_oops=1 to reboot if the kernel panics. Change-Id: Id9e8689a570229db2ea2a3d72b52784f8a1ed107 --- etc/init.rc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/etc/init.rc b/etc/init.rc index 8ed003888..23aca5aa2 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -28,6 +28,8 @@ on init chown root shell /tmp chmod 0775 /tmp + write /proc/sys/kernel/panic_on_oops 1 + on fs mkdir /dev/usb-ffs 0770 shell shell mkdir /dev/usb-ffs/adb 0770 shell shell -- cgit v1.2.3 From 075ad800c539503d0515e5e0b4af160eccedead9 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 26 Jun 2014 15:35:51 -0700 Subject: sideload without holding the whole package in RAM Implement a new method of sideloading over ADB that does not require the entire package to be held in RAM (useful for low-RAM devices and devices using block OTA where we'd rather have more RAM available for binary patching). We communicate with the host using a new adb service called "sideload-host", which makes the host act as a server, sending us different parts of the package file on request. We create a FUSE filesystem that creates a virtual file "/sideload/package.zip" that is backed by the ADB connection -- users see a normal file, but when they read from the file we're actually fetching the data from the adb host. This file is then passed to the verification and installation systems like any other. To prevent a malicious adb host implementation from serving different data to the verification and installation phases of sideloading, the FUSE filesystem verifies that the contents of the file don't change between reads -- every time we fetch a block from the host we compare its hash to the previous hash for that block (if it was read before) and cause the read to fail if it changes. One necessary change is that the minadbd started by recovery in sideload mode no longer drops its root privileges (they're needed to mount the FUSE filesystem). We rely on SELinux enforcement to restrict the set of things that can be accessed. Change-Id: Ida7dbd3b04c1d4e27a2779d88c1da0c7c81fb114 --- adb_install.cpp | 67 +++++-- etc/init.rc | 1 + install.cpp | 10 +- install.h | 2 +- minadbd/Android.mk | 6 +- minadbd/adb.c | 10 - minadbd/adb.h | 8 +- minadbd/fuse_sideload.c | 508 ++++++++++++++++++++++++++++++++++++++++++++++++ minadbd/fuse_sideload.h | 22 +++ minadbd/services.c | 51 ++--- recovery.cpp | 4 +- 11 files changed, 618 insertions(+), 71 deletions(-) create mode 100644 minadbd/fuse_sideload.c create mode 100644 minadbd/fuse_sideload.h diff --git a/adb_install.cpp b/adb_install.cpp index a226ea571..fb7860c9b 100644 --- a/adb_install.cpp +++ b/adb_install.cpp @@ -69,6 +69,10 @@ maybe_restart_adbd() { } } +// How long (in seconds) we wait for the host to start sending us a +// package, before timing out. +#define ADB_INSTALL_TIMEOUT 300 + int apply_from_adb(RecoveryUI* ui_, int* wipe_cache, const char* install_file) { ui = ui_; @@ -84,27 +88,58 @@ apply_from_adb(RecoveryUI* ui_, int* wipe_cache, const char* install_file) { execl("/sbin/recovery", "recovery", "--adbd", NULL); _exit(-1); } + + // ADB_SIDELOAD_HOST_PATHNAME will start to exist once the host + // connects and starts serving a package. Poll for its + // appearance. (Note that inotify doesn't work with FUSE.) + int result; int status; - // TODO(dougz): there should be a way to cancel waiting for a - // package (by pushing some button combo on the device). For now - // you just have to 'adb sideload' a file that's not a valid - // package, like "/dev/null". - waitpid(child, &status, 0); + bool waited = false; + struct stat st; + for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) { + if (waitpid(child, &status, WNOHANG) != 0) { + result = INSTALL_ERROR; + waited = true; + break; + } + + if (stat(ADB_SIDELOAD_HOST_PATHNAME, &st) != 0) { + if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT-1) { + sleep(1); + continue; + } else { + ui->Print("\nTimed out waiting for package.\n\n", strerror(errno)); + result = INSTALL_ERROR; + kill(child, SIGKILL); + break; + } + } + result = install_package(ADB_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false); + break; + } + + if (!waited) { + // Calling stat() on this magic filename signals the minadbd + // subprocess to shut down. + stat(ADB_SIDELOAD_HOST_EXIT_PATHNAME, &st); + + // TODO(dougz): there should be a way to cancel waiting for a + // package (by pushing some button combo on the device). For now + // you just have to 'adb sideload' a file that's not a valid + // package, like "/dev/null". + waitpid(child, &status, 0); + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - ui->Print("status %d\n", WEXITSTATUS(status)); + if (WEXITSTATUS(status) == 3) { + ui->Print("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n"); + } else if (!WIFSIGNALED(status)) { + ui->Print("\n(adbd status %d)\n", WEXITSTATUS(status)); + } } set_usb_driver(false); maybe_restart_adbd(); - struct stat st; - if (stat(ADB_SIDELOAD_FILENAME, &st) != 0) { - if (errno == ENOENT) { - ui->Print("No package received.\n"); - } else { - ui->Print("Error reading package:\n %s\n", strerror(errno)); - } - return INSTALL_ERROR; - } - return install_package(ADB_SIDELOAD_FILENAME, wipe_cache, install_file); + return result; } diff --git a/etc/init.rc b/etc/init.rc index effb6449a..1b402e20d 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -23,6 +23,7 @@ on init mkdir /system mkdir /data mkdir /cache + mkdir /sideload mount tmpfs tmpfs /tmp chown root shell /tmp diff --git a/install.cpp b/install.cpp index 0f059602a..9db5640a0 100644 --- a/install.cpp +++ b/install.cpp @@ -182,7 +182,7 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { } static int -really_install_package(const char *path, int* wipe_cache) +really_install_package(const char *path, int* wipe_cache, bool needs_mount) { ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); ui->Print("Finding update package...\n"); @@ -194,7 +194,7 @@ really_install_package(const char *path, int* wipe_cache) // Map the update package into memory. ui->Print("Opening update package...\n"); - if (path) { + if (path && needs_mount) { if (path[0] == '@') { ensure_path_mounted(path+1); } else { @@ -244,6 +244,7 @@ really_install_package(const char *path, int* wipe_cache) ui->SetEnableReboot(false); int result = try_update_binary(path, &zip, wipe_cache); ui->SetEnableReboot(true); + ui->Print("\n"); sysReleaseMap(&map); @@ -251,7 +252,8 @@ really_install_package(const char *path, int* wipe_cache) } int -install_package(const char* path, int* wipe_cache, const char* install_file) +install_package(const char* path, int* wipe_cache, const char* install_file, + bool needs_mount) { FILE* install_log = fopen_path(install_file, "w"); if (install_log) { @@ -265,7 +267,7 @@ install_package(const char* path, int* wipe_cache, const char* install_file) LOGE("failed to set up expected mounts for install; aborting\n"); result = INSTALL_ERROR; } else { - result = really_install_package(path, wipe_cache); + result = really_install_package(path, wipe_cache, needs_mount); } if (install_log) { fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log); diff --git a/install.h b/install.h index 2ada529ef..53c0d312b 100644 --- a/install.h +++ b/install.h @@ -28,7 +28,7 @@ enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE }; // returned and *wipe_cache is true on exit, caller should wipe the // cache partition. int install_package(const char *root_path, int* wipe_cache, - const char* install_file); + const char* install_file, bool needs_mount); #ifdef __cplusplus } diff --git a/minadbd/Android.mk b/minadbd/Android.mk index 5a4de6828..b5fb8448a 100644 --- a/minadbd/Android.mk +++ b/minadbd/Android.mk @@ -13,6 +13,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ adb.c \ fdevent.c \ + fuse_sideload.c \ transport.c \ transport_usb.c \ sockets.c \ @@ -25,8 +26,5 @@ LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE LOCAL_MODULE := libminadbd -LOCAL_STATIC_LIBRARIES := libcutils libc +LOCAL_STATIC_LIBRARIES := libcutils libc libmincrypt include $(BUILD_STATIC_LIBRARY) - - - diff --git a/minadbd/adb.c b/minadbd/adb.c index 7291b4bd5..127d072be 100644 --- a/minadbd/adb.c +++ b/minadbd/adb.c @@ -392,16 +392,6 @@ int adb_main() usb_init(); } - if (setgid(AID_SHELL) != 0) { - fprintf(stderr, "failed to setgid to shell\n"); - exit(1); - } - if (setuid(AID_SHELL) != 0) { - fprintf(stderr, "failed to setuid to shell\n"); - exit(1); - } - fprintf(stderr, "userid is %d\n", getuid()); - D("Event loop starting\n"); fdevent_loop(); diff --git a/minadbd/adb.h b/minadbd/adb.h index d389165ae..770f34d19 100644 --- a/minadbd/adb.h +++ b/minadbd/adb.h @@ -400,6 +400,7 @@ int connection_state(atransport *t); #define CS_RECOVERY 4 #define CS_NOPERM 5 /* Insufficient permissions to communicate with the device */ #define CS_SIDELOAD 6 +#define CS_UNAUTHORIZED 7 extern int HOST; extern int SHELL_EXIT_NOTIFY_FD; @@ -420,6 +421,11 @@ extern int SHELL_EXIT_NOTIFY_FD; int sendfailmsg(int fd, const char *reason); int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s); -#define ADB_SIDELOAD_FILENAME "/tmp/update.zip" +// define the filenames created by the sideload-host FUSE filesystem +#define ADB_SIDELOAD_HOST_MOUNTPOINT "/sideload" +#define ADB_SIDELOAD_HOST_FILENAME "package.zip" +#define ADB_SIDELOAD_HOST_PATHNAME (ADB_SIDELOAD_HOST_MOUNTPOINT "/" ADB_SIDELOAD_HOST_FILENAME) +#define ADB_SIDELOAD_HOST_EXIT_FLAG "exit" +#define ADB_SIDELOAD_HOST_EXIT_PATHNAME (ADB_SIDELOAD_HOST_MOUNTPOINT "/" ADB_SIDELOAD_HOST_EXIT_FLAG) #endif diff --git a/minadbd/fuse_sideload.c b/minadbd/fuse_sideload.c new file mode 100644 index 000000000..def068e55 --- /dev/null +++ b/minadbd/fuse_sideload.c @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This module creates a special filesystem containing two files. +// +// "/sideload/package.zip" appears to be a normal file, but reading +// from it causes data to be fetched from the adb host. We can use +// this to sideload packages over an adb connection without having to +// store the entire package in RAM on the device. +// +// Because we may not trust the adb host, this filesystem maintains +// the following invariant: each read of a given position returns the +// same data as the first read at that position. That is, once a +// section of the file is read, future reads of that section return +// the same data. (Otherwise, a malicious adb host process could +// return one set of bits when the package is read for signature +// verification, and then different bits for when the package is +// accessed by the installer.) If the adb host returns something +// different than it did on the first read, the reader of the file +// will see their read fail with EINVAL. +// +// The other file, "/sideload/exit", is used to control the subprocess +// that creates this filesystem. Calling stat() on the exit file +// causes the filesystem to be unmounted and the adb process on the +// device shut down. +// +// Note that only the minimal set of file operations needed for these +// two files is implemented. In particular, you can't opendir() or +// readdir() on the "/sideload" directory; ls on it won't work. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "transport.h" +#include "adb.h" +#include "mincrypt/sha256.h" + +#define PACKAGE_FILE_ID (FUSE_ROOT_ID+1) +#define EXIT_FLAG_ID (FUSE_ROOT_ID+2) + +#define NO_STATUS 1 +#define NO_STATUS_EXIT 2 + +struct fuse_data { + int ffd; // file descriptor for the fuse socket + int sfd; // file descriptor for the adb channel + + uint64_t file_size; // bytes + + uint32_t block_size; // block size that the adb host is using to send the file to us + uint32_t file_blocks; // file size in block_size blocks + + uid_t uid; + gid_t gid; + + uint32_t curr_block; // cache the block most recently read from the host + uint8_t* block_data; + + uint8_t* extra_block; // another block of storage for reads that + // span two blocks + + uint8_t* hashes; // SHA-256 hash of each block (all zeros + // if block hasn't been read yet) +}; + +static void fuse_reply(struct fuse_data* fd, __u64 unique, const void *data, size_t len) +{ + struct fuse_out_header hdr; + struct iovec vec[2]; + int res; + + hdr.len = len + sizeof(hdr); + hdr.error = 0; + hdr.unique = unique; + + vec[0].iov_base = &hdr; + vec[0].iov_len = sizeof(hdr); + vec[1].iov_base = data; + vec[1].iov_len = len; + + res = writev(fd->ffd, vec, 2); + if (res < 0) { + printf("*** REPLY FAILED *** %d\n", errno); + } +} + +static int handle_init(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { + const struct fuse_init_in* req = data; + struct fuse_init_out out; + + out.major = FUSE_KERNEL_VERSION; + out.minor = FUSE_KERNEL_MINOR_VERSION; + out.max_readahead = req->max_readahead; + out.flags = 0; + out.max_background = 32; + out.congestion_threshold = 32; + out.max_write = 4096; + fuse_reply(fd, hdr->unique, &out, sizeof(out)); + + return NO_STATUS; +} + +static void fill_attr(struct fuse_attr* attr, struct fuse_data* fd, + uint64_t nodeid, uint64_t size, uint32_t mode) { + memset(attr, 0, sizeof(*attr)); + attr->nlink = 1; + attr->uid = fd->uid; + attr->gid = fd->gid; + attr->blksize = 4096; + + attr->ino = nodeid; + attr->size = size; + attr->blocks = (size == 0) ? 0 : (((size-1) / attr->blksize) + 1); + attr->mode = mode; +} + +static int handle_getattr(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { + const struct fuse_getattr_in* req = data; + struct fuse_attr_out out; + memset(&out, 0, sizeof(out)); + out.attr_valid = 10; + + if (hdr->nodeid == FUSE_ROOT_ID) { + fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555); + } else if (hdr->nodeid == PACKAGE_FILE_ID) { + fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); + } else if (hdr->nodeid == EXIT_FLAG_ID) { + fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); + } else { + return -ENOENT; + } + + fuse_reply(fd, hdr->unique, &out, sizeof(out)); + return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; +} + +static int handle_lookup(void* data, struct fuse_data* fd, + const struct fuse_in_header* hdr) { + struct fuse_entry_out out; + memset(&out, 0, sizeof(out)); + out.entry_valid = 10; + out.attr_valid = 10; + + if (strncmp(ADB_SIDELOAD_HOST_FILENAME, data, + sizeof(ADB_SIDELOAD_HOST_FILENAME)) == 0) { + out.nodeid = PACKAGE_FILE_ID; + out.generation = PACKAGE_FILE_ID; + fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); + } else if (strncmp(ADB_SIDELOAD_HOST_EXIT_FLAG, data, + sizeof(ADB_SIDELOAD_HOST_EXIT_FLAG)) == 0) { + out.nodeid = EXIT_FLAG_ID; + out.generation = EXIT_FLAG_ID; + fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); + } else { + return -ENOENT; + } + + fuse_reply(fd, hdr->unique, &out, sizeof(out)); + return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; +} + +static int handle_open(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { + const struct fuse_open_in* req = data; + + if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM; + if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; + + struct fuse_open_out out; + memset(&out, 0, sizeof(out)); + out.fh = 10; // an arbitrary number; we always use the same handle + fuse_reply(fd, hdr->unique, &out, sizeof(out)); + return NO_STATUS; +} + +static int handle_flush(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { + return 0; +} + +static int handle_release(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { + return 0; +} + +// Fetch a block from the host into fd->curr_block and fd->block_data. +// Returns 0 on successful fetch, negative otherwise. +static int fetch_block(struct fuse_data* fd, uint32_t block) { + if (block == fd->curr_block) { + return 0; + } + + if (block >= fd->file_blocks) { + memset(fd->block_data, 0, fd->block_size); + fd->curr_block = block; + return 0; + } + + size_t fetch_size = fd->block_size; + if (block * fd->block_size + fetch_size > fd->file_size) { + // If we're reading the last (partial) block of the file, + // expect a shorter response from the host, and pad the rest + // of the block with zeroes. + fetch_size = fd->file_size - (block * fd->block_size); + memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size); + } + + char buf[10]; + snprintf(buf, sizeof(buf), "%08u", block); + if (writex(fd->sfd, buf, 8) < 0) { + fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno)); + return -EIO; + } + + if (readx(fd->sfd, fd->block_data, fetch_size) < 0) { + fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno)); + return -EIO; + } + + fd->curr_block = block; + + // Verify the hash of the block we just got from the host. + // + // - If the hash of the just-received data matches the stored hash + // for the block, accept it. + // - If the stored hash is all zeroes, store the new hash and + // accept the block (this is the first time we've read this + // block). + // - Otherwise, return -EINVAL for the read. + + uint8_t hash[SHA256_DIGEST_SIZE]; + SHA256_hash(fd->block_data, fd->block_size, hash); + uint8_t* blockhash = fd->hashes + block * SHA256_DIGEST_SIZE; + if (memcmp(hash, blockhash, SHA256_DIGEST_SIZE) == 0) { + return 0; + } + + int i; + for (i = 0; i < SHA256_DIGEST_SIZE; ++i) { + if (blockhash[i] != 0) { + fd->curr_block = -1; + return -EIO; + } + } + + memcpy(blockhash, hash, SHA256_DIGEST_SIZE); + return 0; +} + +static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { + const struct fuse_read_in* req = data; + struct fuse_out_header outhdr; + struct iovec vec[3]; + int vec_used; + int result; + + if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; + + uint64_t offset = req->offset; + uint32_t size = req->size; + + // The docs on the fuse kernel interface are vague about what to + // do when a read request extends past the end of the file. We + // can return a short read -- the return structure does include a + // length field -- but in testing that caused the program using + // the file to segfault. (I speculate that this is due to the + // reading program accessing it via mmap; maybe mmap dislikes when + // you return something short of a whole page?) To fix this we + // zero-pad reads that extend past the end of the file so we're + // always returning exactly as many bytes as were requested. + // (Users of the mapped file have to know its real length anyway.) + + outhdr.len = sizeof(outhdr) + size; + outhdr.error = 0; + outhdr.unique = hdr->unique; + vec[0].iov_base = &outhdr; + vec[0].iov_len = sizeof(outhdr); + + uint32_t block = offset / fd->block_size; + result = fetch_block(fd, block); + if (result != 0) return result; + + // Two cases: + // + // - the read request is entirely within this block. In this + // case we can reply immediately. + // + // - the read request goes over into the next block. Note that + // since we mount the filesystem with max_read=block_size, a + // read can never span more than two blocks. In this case we + // copy the block to extra_block and issue a fetch for the + // following block. + + uint32_t block_offset = offset - (block * fd->block_size); + + if (size + block_offset <= fd->block_size) { + // First case: the read fits entirely in the first block. + + vec[1].iov_base = fd->block_data + block_offset; + vec[1].iov_len = size; + vec_used = 2; + } else { + // Second case: the read spills over into the next block. + + memcpy(fd->extra_block, fd->block_data + block_offset, + fd->block_size - block_offset); + vec[1].iov_base = fd->extra_block; + vec[1].iov_len = fd->block_size - block_offset; + + result = fetch_block(fd, block+1); + if (result != 0) return result; + vec[2].iov_base = fd->block_data; + vec[2].iov_len = size - vec[1].iov_len; + vec_used = 3; + } + + if (writev(fd->ffd, vec, vec_used) < 0) { + printf("*** READ REPLY FAILED: %s ***\n", strerror(errno)); + } + return NO_STATUS; +} + +int run_fuse(int sfd, uint64_t file_size, uint32_t block_size) +{ + int result; + + // If something's already mounted on our mountpoint, try to remove + // it. (Mostly in case of a previous abnormal exit.) + umount2(ADB_SIDELOAD_HOST_MOUNTPOINT, MNT_FORCE); + + if (block_size < 1024) { + fprintf(stderr, "block size (%u) is too small\n", block_size); + return -1; + } + if (block_size > (1<<22)) { // 4 MiB + fprintf(stderr, "block size (%u) is too large\n", block_size); + return -1; + } + + struct fuse_data fd; + memset(&fd, 0, sizeof(fd)); + fd.sfd = sfd; + fd.file_size = file_size; + fd.block_size = block_size; + fd.file_blocks = (file_size == 0) ? 0 : (((file_size-1) / block_size) + 1); + + if (fd.file_blocks > (1<<18)) { + fprintf(stderr, "file has too many blocks (%u)\n", fd.file_blocks); + result = -1; + goto done; + } + + fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_SIZE); + if (fd.hashes == NULL) { + fprintf(stderr, "failed to allocate %d bites for hashes\n", + fd.file_blocks * SHA256_DIGEST_SIZE); + result = -1; + goto done; + } + + fd.uid = getuid(); + fd.gid = getgid(); + + fd.curr_block = -1; + fd.block_data = (uint8_t*)malloc(block_size); + if (fd.block_data == NULL) { + fprintf(stderr, "failed to allocate %d bites for block_data\n", block_size); + result = -1; + goto done; + } + fd.extra_block = (uint8_t*)malloc(block_size); + if (fd.extra_block == NULL) { + fprintf(stderr, "failed to allocate %d bites for extra_block\n", block_size); + result = -1; + goto done; + } + + fd.ffd = open("/dev/fuse", O_RDWR); + if (fd.ffd < 0) { + perror("open /dev/fuse"); + result = -1; + goto done; + } + + char opts[256]; + snprintf(opts, sizeof(opts), + ("fd=%d,user_id=%d,group_id=%d,max_read=%zu," + "allow_other,rootmode=040000"), + fd.ffd, fd.uid, fd.gid, block_size); + + result = mount("/dev/fuse", ADB_SIDELOAD_HOST_MOUNTPOINT, + "fuse", MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts); + if (result < 0) { + perror("mount"); + goto done; + } + uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX*8]; + for (;;) { + ssize_t len = read(fd.ffd, request_buffer, sizeof(request_buffer)); + if (len < 0) { + if (errno != EINTR) { + perror("read request"); + if (errno == ENODEV) { + result = -1; + break; + } + } + continue; + } + + if ((size_t)len < sizeof(struct fuse_in_header)) { + fprintf(stderr, "request too short: len=%zu\n", (size_t)len); + continue; + } + + struct fuse_in_header* hdr = (struct fuse_in_header*) request_buffer; + void* data = request_buffer + sizeof(struct fuse_in_header); + + result = -ENOSYS; + + switch (hdr->opcode) { + case FUSE_INIT: + result = handle_init(data, &fd, hdr); + break; + + case FUSE_LOOKUP: + result = handle_lookup(data, &fd, hdr); + break; + + case FUSE_GETATTR: + result = handle_getattr(data, &fd, hdr); + break; + + case FUSE_OPEN: + result = handle_open(data, &fd, hdr); + break; + + case FUSE_READ: + result = handle_read(data, &fd, hdr); + break; + + case FUSE_FLUSH: + result = handle_flush(data, &fd, hdr); + break; + + case FUSE_RELEASE: + result = handle_release(data, &fd, hdr); + break; + + default: + fprintf(stderr, "unknown fuse request opcode %d\n", hdr->opcode); + break; + } + + if (result == NO_STATUS_EXIT) { + result = 0; + break; + } + + if (result != NO_STATUS) { + struct fuse_out_header outhdr; + outhdr.len = sizeof(outhdr); + outhdr.error = result; + outhdr.unique = hdr->unique; + write(fd.ffd, &outhdr, sizeof(outhdr)); + } + } + + done: + writex(sfd, "DONEDONE", 8); + result = umount2(ADB_SIDELOAD_HOST_MOUNTPOINT, MNT_DETACH); + if (result < 0) { + printf("fuse_sideload umount failed: %s\n", strerror(errno)); + } + + if (fd.ffd) close(fd.ffd); + free(fd.hashes); + free(fd.block_data); + free(fd.extra_block); + + return result; +} diff --git a/minadbd/fuse_sideload.h b/minadbd/fuse_sideload.h new file mode 100644 index 000000000..caeeec757 --- /dev/null +++ b/minadbd/fuse_sideload.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2014 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 __FUSE_SIDELOAD_H +#define __FUSE_SIDELOAD_H + +int run_fuse(int sfd, uint64_t file_size, uint32_t block_size); + +#endif diff --git a/minadbd/services.c b/minadbd/services.c index 752b33e82..bf57dc372 100644 --- a/minadbd/services.c +++ b/minadbd/services.c @@ -22,6 +22,7 @@ #include "sysdeps.h" #include "fdevent.h" +#include "fuse_sideload.h" #define TRACE_TAG TRACE_SERVICES #include "adb.h" @@ -43,44 +44,23 @@ void *service_bootstrap_func(void *x) return 0; } -static void sideload_service(int s, void *cookie) +static void sideload_host_service(int sfd, void* cookie) { - unsigned char buf[4096]; - unsigned count = (unsigned)(uintptr_t)cookie; - int fd; - - fprintf(stderr, "sideload_service invoked\n"); - - fd = adb_creat(ADB_SIDELOAD_FILENAME, 0644); - if(fd < 0) { - fprintf(stderr, "failed to create %s\n", ADB_SIDELOAD_FILENAME); - adb_close(s); - exit(1); - } + char* saveptr; + const char* s = strtok_r(cookie, ":", &saveptr); + uint64_t file_size = strtoull(s, NULL, 10); + s = strtok_r(NULL, ":", &saveptr); + uint32_t block_size = strtoul(s, NULL, 10); - while(count > 0) { - unsigned xfer = (count > 4096) ? 4096 : count; - if(readx(s, buf, xfer)) break; - if(writex(fd, buf, xfer)) break; - count -= xfer; - } + printf("sideload-host file size %llu block size %lu\n", file_size, block_size); - if(count == 0) { - writex(s, "OKAY", 4); - } else { - writex(s, "FAIL", 4); - } - adb_close(fd); - adb_close(s); + int result = run_fuse(sfd, file_size, block_size); - if (count == 0) { - fprintf(stderr, "adbd exiting after successful sideload\n"); - sleep(1); - exit(0); - } + printf("sideload_host finished\n"); + sleep(1); + exit(result == 0 ? 0 : 1); } - #if 0 static void echo_service(int fd, void *cookie) { @@ -149,7 +129,12 @@ int service_to_fd(const char *name) int ret = -1; if (!strncmp(name, "sideload:", 9)) { - ret = create_service_thread(sideload_service, (void*)(uintptr_t)atoi(name + 9)); + // this exit status causes recovery to print a special error + // message saying to use a newer adb (that supports + // sideload-host). + exit(3); + } else if (!strncmp(name, "sideload-host:", 14)) { + ret = create_service_thread(sideload_host_service, (void*)(name + 14)); #if 0 } else if(!strncmp(name, "echo:", 5)){ ret = create_service_thread(echo_service, 0); diff --git a/recovery.cpp b/recovery.cpp index 09af5f8b1..5a2715cb8 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -712,7 +712,7 @@ update_directory(const char* path, const char* unmount_when_done, ensure_path_unmounted(unmount_when_done); } if (copy) { - result = install_package(copy, wipe_cache, TEMPORARY_INSTALL_FILE); + result = install_package(copy, wipe_cache, TEMPORARY_INSTALL_FILE, true); free(copy); } else { result = INSTALL_ERROR; @@ -1047,7 +1047,7 @@ main(int argc, char **argv) { int status = INSTALL_SUCCESS; if (update_package != NULL) { - status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE); + status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE, true); if (status == INSTALL_SUCCESS && wipe_cache) { if (erase_volume("/cache")) { LOGE("Cache wipe (requested by package) failed."); -- cgit v1.2.3 From 93950229cf9a991589f6bb071a966b00349d18d6 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 8 Jul 2014 14:10:23 -0700 Subject: drop APPLY_CACHE and refactor APPLY_EXT Drop support for sideloading OTA packages of the cache partition (a half-solution that's long since been deprecated by "adb sideload"). Refactor the code to sideload OTA packages from SD cards: remove the installation code from the file browser. Change-Id: Id0dff6b27c4a5837546f174f50e2e1d0379c43db --- device.h | 3 +- recovery.cpp | 108 +++++++++++++++++++++++++---------------------------------- 2 files changed, 47 insertions(+), 64 deletions(-) diff --git a/device.h b/device.h index df71377c1..57ec3fc32 100644 --- a/device.h +++ b/device.h @@ -65,7 +65,8 @@ class Device { // - invoke a specific action (a menu position: any non-negative number) virtual int HandleMenuKey(int key, int visible) = 0; - enum BuiltinAction { NO_ACTION, REBOOT, APPLY_EXT, APPLY_CACHE, + enum BuiltinAction { NO_ACTION, REBOOT, APPLY_EXT, + APPLY_CACHE, // APPLY_CACHE is deprecated; has no effect APPLY_ADB_SIDELOAD, WIPE_DATA, WIPE_CACHE, REBOOT_BOOTLOADER, SHUTDOWN }; diff --git a/recovery.cpp b/recovery.cpp index 5a2715cb8..1c7c0d6a4 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -604,9 +604,9 @@ static int compare_string(const void* a, const void* b) { return strcmp(*(const char**)a, *(const char**)b); } -static int -update_directory(const char* path, const char* unmount_when_done, - int* wipe_cache, Device* device) { +// Returns a malloc'd path, or NULL. +static char* +browse_directory(const char* path, Device* device) { ensure_path_mounted(path); const char* MENU_HEADERS[] = { "Choose a package to install:", @@ -618,10 +618,7 @@ update_directory(const char* path, const char* unmount_when_done, d = opendir(path); if (d == NULL) { LOGE("error opening %s: %s\n", path, strerror(errno)); - if (unmount_when_done != NULL) { - ensure_path_unmounted(unmount_when_done); - } - return 0; + return NULL; } const char** headers = prepend_title(MENU_HEADERS); @@ -677,58 +674,41 @@ update_directory(const char* path, const char* unmount_when_done, z_size += d_size; zips[z_size] = NULL; - int result; + char* result; int chosen_item = 0; - do { + while (true) { chosen_item = get_menu_selection(headers, zips, 1, chosen_item, device); char* item = zips[chosen_item]; int item_len = strlen(item); if (chosen_item == 0) { // item 0 is always "../" // go up but continue browsing (if the caller is update_directory) - result = -1; + result = NULL; break; - } else if (item[item_len-1] == '/') { + } + + char new_path[PATH_MAX]; + strlcpy(new_path, path, PATH_MAX); + strlcat(new_path, "/", PATH_MAX); + strlcat(new_path, item, PATH_MAX); + + if (item[item_len-1] == '/') { // recurse down into a subdirectory - char new_path[PATH_MAX]; - strlcpy(new_path, path, PATH_MAX); - strlcat(new_path, "/", PATH_MAX); - strlcat(new_path, item, PATH_MAX); new_path[strlen(new_path)-1] = '\0'; // truncate the trailing '/' - result = update_directory(new_path, unmount_when_done, wipe_cache, device); - if (result >= 0) break; + result = browse_directory(new_path, device); + if (result) break; } else { - // selected a zip file: attempt to install it, and return - // the status to the caller. - char new_path[PATH_MAX]; - strlcpy(new_path, path, PATH_MAX); - strlcat(new_path, "/", PATH_MAX); - strlcat(new_path, item, PATH_MAX); - - ui->Print("\n-- Install %s ...\n", path); - set_sdcard_update_bootloader_message(); - char* copy = copy_sideloaded_package(new_path); - if (unmount_when_done != NULL) { - ensure_path_unmounted(unmount_when_done); - } - if (copy) { - result = install_package(copy, wipe_cache, TEMPORARY_INSTALL_FILE, true); - free(copy); - } else { - result = INSTALL_ERROR; - } + // selected a zip file: return the malloc'd path to the caller. + result = strdup(new_path); break; } - } while (true); + } int i; for (i = 0; i < z_size; ++i) free(zips[i]); free(zips); free(headers); - if (unmount_when_done != NULL) { - ensure_path_unmounted(unmount_when_done); - } return result; } @@ -800,7 +780,7 @@ prompt_and_wait(Device* device, int status) { // statement below. Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item); - int wipe_cache; + int wipe_cache = 0; switch (chosen_action) { case Device::NO_ACTION: break; @@ -822,8 +802,28 @@ prompt_and_wait(Device* device, int status) { if (!ui->IsTextVisible()) return Device::NO_ACTION; break; - case Device::APPLY_EXT: - status = update_directory(SDCARD_ROOT, SDCARD_ROOT, &wipe_cache, device); + case Device::APPLY_EXT: { + ensure_path_mounted(SDCARD_ROOT); + char* path = browse_directory(SDCARD_ROOT, device); + if (path == NULL) { + ui->Print("\n-- No package file selected.\n", path); + break; + } + + ui->Print("\n-- Install %s ...\n", path); + set_sdcard_update_bootloader_message(); + char* copy = copy_sideloaded_package(path); + free(path); + ensure_path_unmounted(SDCARD_ROOT); + + int status; + if (copy) { + status = install_package(copy, &wipe_cache, TEMPORARY_INSTALL_FILE, true); + free(copy); + } else { + status = INSTALL_ERROR; + } + if (status == INSTALL_SUCCESS && wipe_cache) { ui->Print("\n-- Wiping cache (at package request)...\n"); if (erase_volume("/cache")) { @@ -843,28 +843,10 @@ prompt_and_wait(Device* device, int status) { } } break; + } case Device::APPLY_CACHE: - // Don't unmount cache at the end of this. - status = update_directory(CACHE_ROOT, NULL, &wipe_cache, device); - if (status == INSTALL_SUCCESS && wipe_cache) { - ui->Print("\n-- Wiping cache (at package request)...\n"); - if (erase_volume("/cache")) { - ui->Print("Cache wipe failed.\n"); - } else { - ui->Print("Cache wipe complete.\n"); - } - } - if (status >= 0) { - if (status != INSTALL_SUCCESS) { - ui->SetBackground(RecoveryUI::ERROR); - ui->Print("Installation aborted.\n"); - } else if (!ui->IsTextVisible()) { - return Device::NO_ACTION; // reboot if logs aren't visible - } else { - ui->Print("\nInstall from cache complete.\n"); - } - } + ui->Print("\nAPPLY_CACHE is deprecated.\n"); break; case Device::APPLY_ADB_SIDELOAD: -- cgit v1.2.3 From 18a78e0a162c35756628610307f41179816d3333 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 10 Jul 2014 07:31:46 -0700 Subject: refactor fuse sideloading code Split the adb-specific portions (fetching a block from the adb host and closing the connections) out from the rest of the FUSE filesystem code, so that we can reuse the fuse stuff for installing off sdcards as well. Change-Id: I0ba385fd35999c5f5cad27842bc82024a264dd14 --- Android.mk | 13 ++ adb_install.cpp | 11 +- fuse_sideload.c | 503 +++++++++++++++++++++++++++++++++++++++++++ fuse_sideload.h | 38 ++++ minadbd/Android.mk | 6 +- minadbd/adb.h | 7 - minadbd/fuse_adb_provider.c | 67 ++++++ minadbd/fuse_adb_provider.h | 22 ++ minadbd/fuse_sideload.c | 508 -------------------------------------------- minadbd/fuse_sideload.h | 22 -- minadbd/services.c | 4 +- 11 files changed, 655 insertions(+), 546 deletions(-) create mode 100644 fuse_sideload.c create mode 100644 fuse_sideload.h create mode 100644 minadbd/fuse_adb_provider.c create mode 100644 minadbd/fuse_adb_provider.h delete mode 100644 minadbd/fuse_sideload.c delete mode 100644 minadbd/fuse_sideload.h diff --git a/Android.mk b/Android.mk index 1165acba4..f469182ef 100644 --- a/Android.mk +++ b/Android.mk @@ -15,6 +15,18 @@ LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := fuse_sideload.c + +LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter +LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE + +LOCAL_MODULE := libfusesideload + +LOCAL_STATIC_LIBRARIES := libcutils libc libmincrypt +include $(BUILD_STATIC_LIBRARY) + include $(CLEAR_VARS) LOCAL_SRC_FILES := \ @@ -49,6 +61,7 @@ LOCAL_STATIC_LIBRARIES := \ libmtdutils \ libmincrypt \ libminadbd \ + libfusesideload \ libminui \ libpng \ libfs_mgr \ diff --git a/adb_install.cpp b/adb_install.cpp index fb7860c9b..be3b9a063 100644 --- a/adb_install.cpp +++ b/adb_install.cpp @@ -31,7 +31,8 @@ #include "common.h" #include "adb_install.h" extern "C" { -#include "minadbd/adb.h" +#include "minadbd/fuse_adb_provider.h" +#include "fuse_sideload.h" } static RecoveryUI* ui = NULL; @@ -89,7 +90,7 @@ apply_from_adb(RecoveryUI* ui_, int* wipe_cache, const char* install_file) { _exit(-1); } - // ADB_SIDELOAD_HOST_PATHNAME will start to exist once the host + // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host // connects and starts serving a package. Poll for its // appearance. (Note that inotify doesn't work with FUSE.) int result; @@ -103,7 +104,7 @@ apply_from_adb(RecoveryUI* ui_, int* wipe_cache, const char* install_file) { break; } - if (stat(ADB_SIDELOAD_HOST_PATHNAME, &st) != 0) { + if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) { if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT-1) { sleep(1); continue; @@ -114,14 +115,14 @@ apply_from_adb(RecoveryUI* ui_, int* wipe_cache, const char* install_file) { break; } } - result = install_package(ADB_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false); + result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false); break; } if (!waited) { // Calling stat() on this magic filename signals the minadbd // subprocess to shut down. - stat(ADB_SIDELOAD_HOST_EXIT_PATHNAME, &st); + stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); // TODO(dougz): there should be a way to cancel waiting for a // package (by pushing some button combo on the device). For now diff --git a/fuse_sideload.c b/fuse_sideload.c new file mode 100644 index 000000000..ab91defbf --- /dev/null +++ b/fuse_sideload.c @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This module creates a special filesystem containing two files. +// +// "/sideload/package.zip" appears to be a normal file, but reading +// from it causes data to be fetched from the adb host. We can use +// this to sideload packages over an adb connection without having to +// store the entire package in RAM on the device. +// +// Because we may not trust the adb host, this filesystem maintains +// the following invariant: each read of a given position returns the +// same data as the first read at that position. That is, once a +// section of the file is read, future reads of that section return +// the same data. (Otherwise, a malicious adb host process could +// return one set of bits when the package is read for signature +// verification, and then different bits for when the package is +// accessed by the installer.) If the adb host returns something +// different than it did on the first read, the reader of the file +// will see their read fail with EINVAL. +// +// The other file, "/sideload/exit", is used to control the subprocess +// that creates this filesystem. Calling stat() on the exit file +// causes the filesystem to be unmounted and the adb process on the +// device shut down. +// +// Note that only the minimal set of file operations needed for these +// two files is implemented. In particular, you can't opendir() or +// readdir() on the "/sideload" directory; ls on it won't work. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mincrypt/sha256.h" +#include "fuse_sideload.h" + +#define PACKAGE_FILE_ID (FUSE_ROOT_ID+1) +#define EXIT_FLAG_ID (FUSE_ROOT_ID+2) + +#define NO_STATUS 1 +#define NO_STATUS_EXIT 2 + +struct fuse_data { + int ffd; // file descriptor for the fuse socket + + struct provider_vtab* vtab; + void* cookie; + + uint64_t file_size; // bytes + + uint32_t block_size; // block size that the adb host is using to send the file to us + uint32_t file_blocks; // file size in block_size blocks + + uid_t uid; + gid_t gid; + + uint32_t curr_block; // cache the block most recently read from the host + uint8_t* block_data; + + uint8_t* extra_block; // another block of storage for reads that + // span two blocks + + uint8_t* hashes; // SHA-256 hash of each block (all zeros + // if block hasn't been read yet) +}; + +static void fuse_reply(struct fuse_data* fd, __u64 unique, const void *data, size_t len) +{ + struct fuse_out_header hdr; + struct iovec vec[2]; + int res; + + hdr.len = len + sizeof(hdr); + hdr.error = 0; + hdr.unique = unique; + + vec[0].iov_base = &hdr; + vec[0].iov_len = sizeof(hdr); + vec[1].iov_base = data; + vec[1].iov_len = len; + + res = writev(fd->ffd, vec, 2); + if (res < 0) { + printf("*** REPLY FAILED *** %d\n", errno); + } +} + +static int handle_init(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { + const struct fuse_init_in* req = data; + struct fuse_init_out out; + + out.major = FUSE_KERNEL_VERSION; + out.minor = FUSE_KERNEL_MINOR_VERSION; + out.max_readahead = req->max_readahead; + out.flags = 0; + out.max_background = 32; + out.congestion_threshold = 32; + out.max_write = 4096; + fuse_reply(fd, hdr->unique, &out, sizeof(out)); + + return NO_STATUS; +} + +static void fill_attr(struct fuse_attr* attr, struct fuse_data* fd, + uint64_t nodeid, uint64_t size, uint32_t mode) { + memset(attr, 0, sizeof(*attr)); + attr->nlink = 1; + attr->uid = fd->uid; + attr->gid = fd->gid; + attr->blksize = 4096; + + attr->ino = nodeid; + attr->size = size; + attr->blocks = (size == 0) ? 0 : (((size-1) / attr->blksize) + 1); + attr->mode = mode; +} + +static int handle_getattr(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { + const struct fuse_getattr_in* req = data; + struct fuse_attr_out out; + memset(&out, 0, sizeof(out)); + out.attr_valid = 10; + + if (hdr->nodeid == FUSE_ROOT_ID) { + fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555); + } else if (hdr->nodeid == PACKAGE_FILE_ID) { + fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); + } else if (hdr->nodeid == EXIT_FLAG_ID) { + fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); + } else { + return -ENOENT; + } + + fuse_reply(fd, hdr->unique, &out, sizeof(out)); + return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; +} + +static int handle_lookup(void* data, struct fuse_data* fd, + const struct fuse_in_header* hdr) { + struct fuse_entry_out out; + memset(&out, 0, sizeof(out)); + out.entry_valid = 10; + out.attr_valid = 10; + + if (strncmp(FUSE_SIDELOAD_HOST_FILENAME, data, + sizeof(FUSE_SIDELOAD_HOST_FILENAME)) == 0) { + out.nodeid = PACKAGE_FILE_ID; + out.generation = PACKAGE_FILE_ID; + fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); + } else if (strncmp(FUSE_SIDELOAD_HOST_EXIT_FLAG, data, + sizeof(FUSE_SIDELOAD_HOST_EXIT_FLAG)) == 0) { + out.nodeid = EXIT_FLAG_ID; + out.generation = EXIT_FLAG_ID; + fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); + } else { + return -ENOENT; + } + + fuse_reply(fd, hdr->unique, &out, sizeof(out)); + return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; +} + +static int handle_open(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { + const struct fuse_open_in* req = data; + + if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM; + if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; + + struct fuse_open_out out; + memset(&out, 0, sizeof(out)); + out.fh = 10; // an arbitrary number; we always use the same handle + fuse_reply(fd, hdr->unique, &out, sizeof(out)); + return NO_STATUS; +} + +static int handle_flush(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { + return 0; +} + +static int handle_release(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { + return 0; +} + +// Fetch a block from the host into fd->curr_block and fd->block_data. +// Returns 0 on successful fetch, negative otherwise. +static int fetch_block(struct fuse_data* fd, uint32_t block) { + if (block == fd->curr_block) { + return 0; + } + + if (block >= fd->file_blocks) { + memset(fd->block_data, 0, fd->block_size); + fd->curr_block = block; + return 0; + } + + size_t fetch_size = fd->block_size; + if (block * fd->block_size + fetch_size > fd->file_size) { + // If we're reading the last (partial) block of the file, + // expect a shorter response from the host, and pad the rest + // of the block with zeroes. + fetch_size = fd->file_size - (block * fd->block_size); + memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size); + } + + int result = fd->vtab->read_block(fd->cookie, block, fd->block_data, fetch_size); + if (result < 0) return result; + + fd->curr_block = block; + + // Verify the hash of the block we just got from the host. + // + // - If the hash of the just-received data matches the stored hash + // for the block, accept it. + // - If the stored hash is all zeroes, store the new hash and + // accept the block (this is the first time we've read this + // block). + // - Otherwise, return -EINVAL for the read. + + uint8_t hash[SHA256_DIGEST_SIZE]; + SHA256_hash(fd->block_data, fd->block_size, hash); + uint8_t* blockhash = fd->hashes + block * SHA256_DIGEST_SIZE; + if (memcmp(hash, blockhash, SHA256_DIGEST_SIZE) == 0) { + return 0; + } + + int i; + for (i = 0; i < SHA256_DIGEST_SIZE; ++i) { + if (blockhash[i] != 0) { + fd->curr_block = -1; + return -EIO; + } + } + + memcpy(blockhash, hash, SHA256_DIGEST_SIZE); + return 0; +} + +static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { + const struct fuse_read_in* req = data; + struct fuse_out_header outhdr; + struct iovec vec[3]; + int vec_used; + int result; + + if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; + + uint64_t offset = req->offset; + uint32_t size = req->size; + + // The docs on the fuse kernel interface are vague about what to + // do when a read request extends past the end of the file. We + // can return a short read -- the return structure does include a + // length field -- but in testing that caused the program using + // the file to segfault. (I speculate that this is due to the + // reading program accessing it via mmap; maybe mmap dislikes when + // you return something short of a whole page?) To fix this we + // zero-pad reads that extend past the end of the file so we're + // always returning exactly as many bytes as were requested. + // (Users of the mapped file have to know its real length anyway.) + + outhdr.len = sizeof(outhdr) + size; + outhdr.error = 0; + outhdr.unique = hdr->unique; + vec[0].iov_base = &outhdr; + vec[0].iov_len = sizeof(outhdr); + + uint32_t block = offset / fd->block_size; + result = fetch_block(fd, block); + if (result != 0) return result; + + // Two cases: + // + // - the read request is entirely within this block. In this + // case we can reply immediately. + // + // - the read request goes over into the next block. Note that + // since we mount the filesystem with max_read=block_size, a + // read can never span more than two blocks. In this case we + // copy the block to extra_block and issue a fetch for the + // following block. + + uint32_t block_offset = offset - (block * fd->block_size); + + if (size + block_offset <= fd->block_size) { + // First case: the read fits entirely in the first block. + + vec[1].iov_base = fd->block_data + block_offset; + vec[1].iov_len = size; + vec_used = 2; + } else { + // Second case: the read spills over into the next block. + + memcpy(fd->extra_block, fd->block_data + block_offset, + fd->block_size - block_offset); + vec[1].iov_base = fd->extra_block; + vec[1].iov_len = fd->block_size - block_offset; + + result = fetch_block(fd, block+1); + if (result != 0) return result; + vec[2].iov_base = fd->block_data; + vec[2].iov_len = size - vec[1].iov_len; + vec_used = 3; + } + + if (writev(fd->ffd, vec, vec_used) < 0) { + printf("*** READ REPLY FAILED: %s ***\n", strerror(errno)); + } + return NO_STATUS; +} + +int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, + uint64_t file_size, uint32_t block_size) +{ + int result; + + // If something's already mounted on our mountpoint, try to remove + // it. (Mostly in case of a previous abnormal exit.) + umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_FORCE); + + if (block_size < 1024) { + fprintf(stderr, "block size (%u) is too small\n", block_size); + return -1; + } + if (block_size > (1<<22)) { // 4 MiB + fprintf(stderr, "block size (%u) is too large\n", block_size); + return -1; + } + + struct fuse_data fd; + memset(&fd, 0, sizeof(fd)); + fd.vtab = vtab; + fd.cookie = cookie; + fd.file_size = file_size; + fd.block_size = block_size; + fd.file_blocks = (file_size == 0) ? 0 : (((file_size-1) / block_size) + 1); + + if (fd.file_blocks > (1<<18)) { + fprintf(stderr, "file has too many blocks (%u)\n", fd.file_blocks); + result = -1; + goto done; + } + + fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_SIZE); + if (fd.hashes == NULL) { + fprintf(stderr, "failed to allocate %d bites for hashes\n", + fd.file_blocks * SHA256_DIGEST_SIZE); + result = -1; + goto done; + } + + fd.uid = getuid(); + fd.gid = getgid(); + + fd.curr_block = -1; + fd.block_data = (uint8_t*)malloc(block_size); + if (fd.block_data == NULL) { + fprintf(stderr, "failed to allocate %d bites for block_data\n", block_size); + result = -1; + goto done; + } + fd.extra_block = (uint8_t*)malloc(block_size); + if (fd.extra_block == NULL) { + fprintf(stderr, "failed to allocate %d bites for extra_block\n", block_size); + result = -1; + goto done; + } + + fd.ffd = open("/dev/fuse", O_RDWR); + if (fd.ffd < 0) { + perror("open /dev/fuse"); + result = -1; + goto done; + } + + char opts[256]; + snprintf(opts, sizeof(opts), + ("fd=%d,user_id=%d,group_id=%d,max_read=%zu," + "allow_other,rootmode=040000"), + fd.ffd, fd.uid, fd.gid, block_size); + + result = mount("/dev/fuse", FUSE_SIDELOAD_HOST_MOUNTPOINT, + "fuse", MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts); + if (result < 0) { + perror("mount"); + goto done; + } + uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX*8]; + for (;;) { + ssize_t len = read(fd.ffd, request_buffer, sizeof(request_buffer)); + if (len < 0) { + if (errno != EINTR) { + perror("read request"); + if (errno == ENODEV) { + result = -1; + break; + } + } + continue; + } + + if ((size_t)len < sizeof(struct fuse_in_header)) { + fprintf(stderr, "request too short: len=%zu\n", (size_t)len); + continue; + } + + struct fuse_in_header* hdr = (struct fuse_in_header*) request_buffer; + void* data = request_buffer + sizeof(struct fuse_in_header); + + result = -ENOSYS; + + switch (hdr->opcode) { + case FUSE_INIT: + result = handle_init(data, &fd, hdr); + break; + + case FUSE_LOOKUP: + result = handle_lookup(data, &fd, hdr); + break; + + case FUSE_GETATTR: + result = handle_getattr(data, &fd, hdr); + break; + + case FUSE_OPEN: + result = handle_open(data, &fd, hdr); + break; + + case FUSE_READ: + result = handle_read(data, &fd, hdr); + break; + + case FUSE_FLUSH: + result = handle_flush(data, &fd, hdr); + break; + + case FUSE_RELEASE: + result = handle_release(data, &fd, hdr); + break; + + default: + fprintf(stderr, "unknown fuse request opcode %d\n", hdr->opcode); + break; + } + + if (result == NO_STATUS_EXIT) { + result = 0; + break; + } + + if (result != NO_STATUS) { + struct fuse_out_header outhdr; + outhdr.len = sizeof(outhdr); + outhdr.error = result; + outhdr.unique = hdr->unique; + write(fd.ffd, &outhdr, sizeof(outhdr)); + } + } + + done: + fd.vtab->close(fd.cookie); + + result = umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_DETACH); + if (result < 0) { + printf("fuse_sideload umount failed: %s\n", strerror(errno)); + } + + if (fd.ffd) close(fd.ffd); + free(fd.hashes); + free(fd.block_data); + free(fd.extra_block); + + return result; +} diff --git a/fuse_sideload.h b/fuse_sideload.h new file mode 100644 index 000000000..c0b16efbe --- /dev/null +++ b/fuse_sideload.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 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 __FUSE_SIDELOAD_H +#define __FUSE_SIDELOAD_H + +// define the filenames created by the sideload FUSE filesystem +#define FUSE_SIDELOAD_HOST_MOUNTPOINT "/sideload" +#define FUSE_SIDELOAD_HOST_FILENAME "package.zip" +#define FUSE_SIDELOAD_HOST_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_FILENAME) +#define FUSE_SIDELOAD_HOST_EXIT_FLAG "exit" +#define FUSE_SIDELOAD_HOST_EXIT_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_EXIT_FLAG) + +struct provider_vtab { + // read a block + int (*read_block)(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size); + + // close down + void (*close)(void* cookie); +}; + +int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, + uint64_t file_size, uint32_t block_size); + +#endif diff --git a/minadbd/Android.mk b/minadbd/Android.mk index b5fb8448a..04956d870 100644 --- a/minadbd/Android.mk +++ b/minadbd/Android.mk @@ -13,7 +13,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ adb.c \ fdevent.c \ - fuse_sideload.c \ + fuse_adb_provider.c \ transport.c \ transport_usb.c \ sockets.c \ @@ -23,8 +23,10 @@ LOCAL_SRC_FILES := \ LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE +LOCAL_C_INCLUDES += bootable/recovery LOCAL_MODULE := libminadbd -LOCAL_STATIC_LIBRARIES := libcutils libc libmincrypt +LOCAL_STATIC_LIBRARIES := libfusesideload libcutils libc + include $(BUILD_STATIC_LIBRARY) diff --git a/minadbd/adb.h b/minadbd/adb.h index 770f34d19..714868f5c 100644 --- a/minadbd/adb.h +++ b/minadbd/adb.h @@ -421,11 +421,4 @@ extern int SHELL_EXIT_NOTIFY_FD; int sendfailmsg(int fd, const char *reason); int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s); -// define the filenames created by the sideload-host FUSE filesystem -#define ADB_SIDELOAD_HOST_MOUNTPOINT "/sideload" -#define ADB_SIDELOAD_HOST_FILENAME "package.zip" -#define ADB_SIDELOAD_HOST_PATHNAME (ADB_SIDELOAD_HOST_MOUNTPOINT "/" ADB_SIDELOAD_HOST_FILENAME) -#define ADB_SIDELOAD_HOST_EXIT_FLAG "exit" -#define ADB_SIDELOAD_HOST_EXIT_PATHNAME (ADB_SIDELOAD_HOST_MOUNTPOINT "/" ADB_SIDELOAD_HOST_EXIT_FLAG) - #endif diff --git a/minadbd/fuse_adb_provider.c b/minadbd/fuse_adb_provider.c new file mode 100644 index 000000000..f80533a8c --- /dev/null +++ b/minadbd/fuse_adb_provider.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2014 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 +#include +#include + +#include "adb.h" +#include "fuse_sideload.h" + +struct adb_data { + int sfd; // file descriptor for the adb channel + + uint64_t file_size; + uint32_t block_size; +}; + +static int read_block_adb(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size) { + struct adb_data* ad = (struct adb_data*)cookie; + + char buf[10]; + snprintf(buf, sizeof(buf), "%08u", block); + if (writex(ad->sfd, buf, 8) < 0) { + fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno)); + return -EIO; + } + + if (readx(ad->sfd, buffer, fetch_size) < 0) { + fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno)); + return -EIO; + } + + return 0; +} + +static void close_adb(void* cookie) { + struct adb_data* ad = (struct adb_data*)cookie; + + writex(ad->sfd, "DONEDONE", 8); +} + +int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size) { + struct adb_data ad; + struct provider_vtab vtab; + + ad.sfd = sfd; + ad.file_size = file_size; + ad.block_size = block_size; + + vtab.read_block = read_block_adb; + vtab.close = close_adb; + + return run_fuse_sideload(&vtab, &ad, file_size, block_size); +} diff --git a/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h new file mode 100644 index 000000000..0eb1f79d1 --- /dev/null +++ b/minadbd/fuse_adb_provider.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2014 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 __FUSE_ADB_PROVIDER_H +#define __FUSE_ADB_PROVIDER_H + +int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size); + +#endif diff --git a/minadbd/fuse_sideload.c b/minadbd/fuse_sideload.c deleted file mode 100644 index def068e55..000000000 --- a/minadbd/fuse_sideload.c +++ /dev/null @@ -1,508 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// This module creates a special filesystem containing two files. -// -// "/sideload/package.zip" appears to be a normal file, but reading -// from it causes data to be fetched from the adb host. We can use -// this to sideload packages over an adb connection without having to -// store the entire package in RAM on the device. -// -// Because we may not trust the adb host, this filesystem maintains -// the following invariant: each read of a given position returns the -// same data as the first read at that position. That is, once a -// section of the file is read, future reads of that section return -// the same data. (Otherwise, a malicious adb host process could -// return one set of bits when the package is read for signature -// verification, and then different bits for when the package is -// accessed by the installer.) If the adb host returns something -// different than it did on the first read, the reader of the file -// will see their read fail with EINVAL. -// -// The other file, "/sideload/exit", is used to control the subprocess -// that creates this filesystem. Calling stat() on the exit file -// causes the filesystem to be unmounted and the adb process on the -// device shut down. -// -// Note that only the minimal set of file operations needed for these -// two files is implemented. In particular, you can't opendir() or -// readdir() on the "/sideload" directory; ls on it won't work. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "transport.h" -#include "adb.h" -#include "mincrypt/sha256.h" - -#define PACKAGE_FILE_ID (FUSE_ROOT_ID+1) -#define EXIT_FLAG_ID (FUSE_ROOT_ID+2) - -#define NO_STATUS 1 -#define NO_STATUS_EXIT 2 - -struct fuse_data { - int ffd; // file descriptor for the fuse socket - int sfd; // file descriptor for the adb channel - - uint64_t file_size; // bytes - - uint32_t block_size; // block size that the adb host is using to send the file to us - uint32_t file_blocks; // file size in block_size blocks - - uid_t uid; - gid_t gid; - - uint32_t curr_block; // cache the block most recently read from the host - uint8_t* block_data; - - uint8_t* extra_block; // another block of storage for reads that - // span two blocks - - uint8_t* hashes; // SHA-256 hash of each block (all zeros - // if block hasn't been read yet) -}; - -static void fuse_reply(struct fuse_data* fd, __u64 unique, const void *data, size_t len) -{ - struct fuse_out_header hdr; - struct iovec vec[2]; - int res; - - hdr.len = len + sizeof(hdr); - hdr.error = 0; - hdr.unique = unique; - - vec[0].iov_base = &hdr; - vec[0].iov_len = sizeof(hdr); - vec[1].iov_base = data; - vec[1].iov_len = len; - - res = writev(fd->ffd, vec, 2); - if (res < 0) { - printf("*** REPLY FAILED *** %d\n", errno); - } -} - -static int handle_init(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { - const struct fuse_init_in* req = data; - struct fuse_init_out out; - - out.major = FUSE_KERNEL_VERSION; - out.minor = FUSE_KERNEL_MINOR_VERSION; - out.max_readahead = req->max_readahead; - out.flags = 0; - out.max_background = 32; - out.congestion_threshold = 32; - out.max_write = 4096; - fuse_reply(fd, hdr->unique, &out, sizeof(out)); - - return NO_STATUS; -} - -static void fill_attr(struct fuse_attr* attr, struct fuse_data* fd, - uint64_t nodeid, uint64_t size, uint32_t mode) { - memset(attr, 0, sizeof(*attr)); - attr->nlink = 1; - attr->uid = fd->uid; - attr->gid = fd->gid; - attr->blksize = 4096; - - attr->ino = nodeid; - attr->size = size; - attr->blocks = (size == 0) ? 0 : (((size-1) / attr->blksize) + 1); - attr->mode = mode; -} - -static int handle_getattr(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { - const struct fuse_getattr_in* req = data; - struct fuse_attr_out out; - memset(&out, 0, sizeof(out)); - out.attr_valid = 10; - - if (hdr->nodeid == FUSE_ROOT_ID) { - fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555); - } else if (hdr->nodeid == PACKAGE_FILE_ID) { - fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); - } else if (hdr->nodeid == EXIT_FLAG_ID) { - fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); - } else { - return -ENOENT; - } - - fuse_reply(fd, hdr->unique, &out, sizeof(out)); - return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; -} - -static int handle_lookup(void* data, struct fuse_data* fd, - const struct fuse_in_header* hdr) { - struct fuse_entry_out out; - memset(&out, 0, sizeof(out)); - out.entry_valid = 10; - out.attr_valid = 10; - - if (strncmp(ADB_SIDELOAD_HOST_FILENAME, data, - sizeof(ADB_SIDELOAD_HOST_FILENAME)) == 0) { - out.nodeid = PACKAGE_FILE_ID; - out.generation = PACKAGE_FILE_ID; - fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); - } else if (strncmp(ADB_SIDELOAD_HOST_EXIT_FLAG, data, - sizeof(ADB_SIDELOAD_HOST_EXIT_FLAG)) == 0) { - out.nodeid = EXIT_FLAG_ID; - out.generation = EXIT_FLAG_ID; - fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); - } else { - return -ENOENT; - } - - fuse_reply(fd, hdr->unique, &out, sizeof(out)); - return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; -} - -static int handle_open(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { - const struct fuse_open_in* req = data; - - if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM; - if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; - - struct fuse_open_out out; - memset(&out, 0, sizeof(out)); - out.fh = 10; // an arbitrary number; we always use the same handle - fuse_reply(fd, hdr->unique, &out, sizeof(out)); - return NO_STATUS; -} - -static int handle_flush(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { - return 0; -} - -static int handle_release(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { - return 0; -} - -// Fetch a block from the host into fd->curr_block and fd->block_data. -// Returns 0 on successful fetch, negative otherwise. -static int fetch_block(struct fuse_data* fd, uint32_t block) { - if (block == fd->curr_block) { - return 0; - } - - if (block >= fd->file_blocks) { - memset(fd->block_data, 0, fd->block_size); - fd->curr_block = block; - return 0; - } - - size_t fetch_size = fd->block_size; - if (block * fd->block_size + fetch_size > fd->file_size) { - // If we're reading the last (partial) block of the file, - // expect a shorter response from the host, and pad the rest - // of the block with zeroes. - fetch_size = fd->file_size - (block * fd->block_size); - memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size); - } - - char buf[10]; - snprintf(buf, sizeof(buf), "%08u", block); - if (writex(fd->sfd, buf, 8) < 0) { - fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno)); - return -EIO; - } - - if (readx(fd->sfd, fd->block_data, fetch_size) < 0) { - fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno)); - return -EIO; - } - - fd->curr_block = block; - - // Verify the hash of the block we just got from the host. - // - // - If the hash of the just-received data matches the stored hash - // for the block, accept it. - // - If the stored hash is all zeroes, store the new hash and - // accept the block (this is the first time we've read this - // block). - // - Otherwise, return -EINVAL for the read. - - uint8_t hash[SHA256_DIGEST_SIZE]; - SHA256_hash(fd->block_data, fd->block_size, hash); - uint8_t* blockhash = fd->hashes + block * SHA256_DIGEST_SIZE; - if (memcmp(hash, blockhash, SHA256_DIGEST_SIZE) == 0) { - return 0; - } - - int i; - for (i = 0; i < SHA256_DIGEST_SIZE; ++i) { - if (blockhash[i] != 0) { - fd->curr_block = -1; - return -EIO; - } - } - - memcpy(blockhash, hash, SHA256_DIGEST_SIZE); - return 0; -} - -static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { - const struct fuse_read_in* req = data; - struct fuse_out_header outhdr; - struct iovec vec[3]; - int vec_used; - int result; - - if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; - - uint64_t offset = req->offset; - uint32_t size = req->size; - - // The docs on the fuse kernel interface are vague about what to - // do when a read request extends past the end of the file. We - // can return a short read -- the return structure does include a - // length field -- but in testing that caused the program using - // the file to segfault. (I speculate that this is due to the - // reading program accessing it via mmap; maybe mmap dislikes when - // you return something short of a whole page?) To fix this we - // zero-pad reads that extend past the end of the file so we're - // always returning exactly as many bytes as were requested. - // (Users of the mapped file have to know its real length anyway.) - - outhdr.len = sizeof(outhdr) + size; - outhdr.error = 0; - outhdr.unique = hdr->unique; - vec[0].iov_base = &outhdr; - vec[0].iov_len = sizeof(outhdr); - - uint32_t block = offset / fd->block_size; - result = fetch_block(fd, block); - if (result != 0) return result; - - // Two cases: - // - // - the read request is entirely within this block. In this - // case we can reply immediately. - // - // - the read request goes over into the next block. Note that - // since we mount the filesystem with max_read=block_size, a - // read can never span more than two blocks. In this case we - // copy the block to extra_block and issue a fetch for the - // following block. - - uint32_t block_offset = offset - (block * fd->block_size); - - if (size + block_offset <= fd->block_size) { - // First case: the read fits entirely in the first block. - - vec[1].iov_base = fd->block_data + block_offset; - vec[1].iov_len = size; - vec_used = 2; - } else { - // Second case: the read spills over into the next block. - - memcpy(fd->extra_block, fd->block_data + block_offset, - fd->block_size - block_offset); - vec[1].iov_base = fd->extra_block; - vec[1].iov_len = fd->block_size - block_offset; - - result = fetch_block(fd, block+1); - if (result != 0) return result; - vec[2].iov_base = fd->block_data; - vec[2].iov_len = size - vec[1].iov_len; - vec_used = 3; - } - - if (writev(fd->ffd, vec, vec_used) < 0) { - printf("*** READ REPLY FAILED: %s ***\n", strerror(errno)); - } - return NO_STATUS; -} - -int run_fuse(int sfd, uint64_t file_size, uint32_t block_size) -{ - int result; - - // If something's already mounted on our mountpoint, try to remove - // it. (Mostly in case of a previous abnormal exit.) - umount2(ADB_SIDELOAD_HOST_MOUNTPOINT, MNT_FORCE); - - if (block_size < 1024) { - fprintf(stderr, "block size (%u) is too small\n", block_size); - return -1; - } - if (block_size > (1<<22)) { // 4 MiB - fprintf(stderr, "block size (%u) is too large\n", block_size); - return -1; - } - - struct fuse_data fd; - memset(&fd, 0, sizeof(fd)); - fd.sfd = sfd; - fd.file_size = file_size; - fd.block_size = block_size; - fd.file_blocks = (file_size == 0) ? 0 : (((file_size-1) / block_size) + 1); - - if (fd.file_blocks > (1<<18)) { - fprintf(stderr, "file has too many blocks (%u)\n", fd.file_blocks); - result = -1; - goto done; - } - - fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_SIZE); - if (fd.hashes == NULL) { - fprintf(stderr, "failed to allocate %d bites for hashes\n", - fd.file_blocks * SHA256_DIGEST_SIZE); - result = -1; - goto done; - } - - fd.uid = getuid(); - fd.gid = getgid(); - - fd.curr_block = -1; - fd.block_data = (uint8_t*)malloc(block_size); - if (fd.block_data == NULL) { - fprintf(stderr, "failed to allocate %d bites for block_data\n", block_size); - result = -1; - goto done; - } - fd.extra_block = (uint8_t*)malloc(block_size); - if (fd.extra_block == NULL) { - fprintf(stderr, "failed to allocate %d bites for extra_block\n", block_size); - result = -1; - goto done; - } - - fd.ffd = open("/dev/fuse", O_RDWR); - if (fd.ffd < 0) { - perror("open /dev/fuse"); - result = -1; - goto done; - } - - char opts[256]; - snprintf(opts, sizeof(opts), - ("fd=%d,user_id=%d,group_id=%d,max_read=%zu," - "allow_other,rootmode=040000"), - fd.ffd, fd.uid, fd.gid, block_size); - - result = mount("/dev/fuse", ADB_SIDELOAD_HOST_MOUNTPOINT, - "fuse", MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts); - if (result < 0) { - perror("mount"); - goto done; - } - uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX*8]; - for (;;) { - ssize_t len = read(fd.ffd, request_buffer, sizeof(request_buffer)); - if (len < 0) { - if (errno != EINTR) { - perror("read request"); - if (errno == ENODEV) { - result = -1; - break; - } - } - continue; - } - - if ((size_t)len < sizeof(struct fuse_in_header)) { - fprintf(stderr, "request too short: len=%zu\n", (size_t)len); - continue; - } - - struct fuse_in_header* hdr = (struct fuse_in_header*) request_buffer; - void* data = request_buffer + sizeof(struct fuse_in_header); - - result = -ENOSYS; - - switch (hdr->opcode) { - case FUSE_INIT: - result = handle_init(data, &fd, hdr); - break; - - case FUSE_LOOKUP: - result = handle_lookup(data, &fd, hdr); - break; - - case FUSE_GETATTR: - result = handle_getattr(data, &fd, hdr); - break; - - case FUSE_OPEN: - result = handle_open(data, &fd, hdr); - break; - - case FUSE_READ: - result = handle_read(data, &fd, hdr); - break; - - case FUSE_FLUSH: - result = handle_flush(data, &fd, hdr); - break; - - case FUSE_RELEASE: - result = handle_release(data, &fd, hdr); - break; - - default: - fprintf(stderr, "unknown fuse request opcode %d\n", hdr->opcode); - break; - } - - if (result == NO_STATUS_EXIT) { - result = 0; - break; - } - - if (result != NO_STATUS) { - struct fuse_out_header outhdr; - outhdr.len = sizeof(outhdr); - outhdr.error = result; - outhdr.unique = hdr->unique; - write(fd.ffd, &outhdr, sizeof(outhdr)); - } - } - - done: - writex(sfd, "DONEDONE", 8); - result = umount2(ADB_SIDELOAD_HOST_MOUNTPOINT, MNT_DETACH); - if (result < 0) { - printf("fuse_sideload umount failed: %s\n", strerror(errno)); - } - - if (fd.ffd) close(fd.ffd); - free(fd.hashes); - free(fd.block_data); - free(fd.extra_block); - - return result; -} diff --git a/minadbd/fuse_sideload.h b/minadbd/fuse_sideload.h deleted file mode 100644 index caeeec757..000000000 --- a/minadbd/fuse_sideload.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2014 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 __FUSE_SIDELOAD_H -#define __FUSE_SIDELOAD_H - -int run_fuse(int sfd, uint64_t file_size, uint32_t block_size); - -#endif diff --git a/minadbd/services.c b/minadbd/services.c index bf57dc372..218b84a38 100644 --- a/minadbd/services.c +++ b/minadbd/services.c @@ -22,7 +22,7 @@ #include "sysdeps.h" #include "fdevent.h" -#include "fuse_sideload.h" +#include "fuse_adb_provider.h" #define TRACE_TAG TRACE_SERVICES #include "adb.h" @@ -54,7 +54,7 @@ static void sideload_host_service(int sfd, void* cookie) printf("sideload-host file size %llu block size %lu\n", file_size, block_size); - int result = run_fuse(sfd, file_size, block_size); + int result = run_adb_fuse(sfd, file_size, block_size); printf("sideload_host finished\n"); sleep(1); -- cgit v1.2.3 From 945fc68c62692467ddb8b7d714bcf0bf01c783c2 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 10 Jul 2014 10:50:39 -0700 Subject: do sdcard sideloading through the fuse filesystem Make a fuse filesystem that sits on top of the selected package file on the sdcard, so we can verify that the file contents don't change while being read and avoid copying the file to /tmp (that is, RAM) before verifying and installing it. Change-Id: Ifd982aa68bfe469eda5f839042648654bf7386a1 --- Android.mk | 3 +- fuse_sdcard_provider.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++ fuse_sdcard_provider.h | 23 ++++++++ recovery.cpp | 110 ++++---------------------------------- 4 files changed, 175 insertions(+), 102 deletions(-) create mode 100644 fuse_sdcard_provider.c create mode 100644 fuse_sdcard_provider.h diff --git a/Android.mk b/Android.mk index f469182ef..1a91f0029 100644 --- a/Android.mk +++ b/Android.mk @@ -38,7 +38,8 @@ LOCAL_SRC_FILES := \ screen_ui.cpp \ asn1_decoder.cpp \ verifier.cpp \ - adb_install.cpp + adb_install.cpp \ + fuse_sdcard_provider.c LOCAL_MODULE := recovery diff --git a/fuse_sdcard_provider.c b/fuse_sdcard_provider.c new file mode 100644 index 000000000..19fb52df0 --- /dev/null +++ b/fuse_sdcard_provider.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2014 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 +#include +#include +#include +#include +#include +#include +#include + +#include "fuse_sideload.h" + +struct file_data { + int fd; // the underlying sdcard file + + uint64_t file_size; + uint32_t block_size; +}; + +static int read_block_file(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size) { + struct file_data* fd = (struct file_data*)cookie; + + if (lseek(fd->fd, block * fd->block_size, SEEK_SET) < 0) { + printf("seek on sdcard failed: %s\n", strerror(errno)); + return -EIO; + } + + while (fetch_size > 0) { + ssize_t r = read(fd->fd, buffer, fetch_size); + if (r < 0) { + if (r != -EINTR) { + printf("read on sdcard failed: %s\n", strerror(errno)); + return -EIO; + } + r = 0; + } + fetch_size -= r; + buffer += r; + } + + return 0; +} + +static void close_file(void* cookie) { + struct file_data* fd = (struct file_data*)cookie; + close(fd->fd); +} + +struct token { + pthread_t th; + const char* path; + int result; +}; + +static void* run_sdcard_fuse(void* cookie) { + struct token* t = (struct token*)cookie; + + struct stat sb; + if (stat(t->path, &sb) < 0) { + fprintf(stderr, "failed to stat %s: %s\n", t->path, strerror(errno)); + t->result = -1; + return NULL; + } + + struct file_data fd; + struct provider_vtab vtab; + + fd.fd = open(t->path, O_RDONLY); + if (fd.fd < 0) { + fprintf(stderr, "failed to open %s: %s\n", t->path, strerror(errno)); + t->result = -1; + return NULL; + } + fd.file_size = sb.st_size; + fd.block_size = 65536; + + vtab.read_block = read_block_file; + vtab.close = close_file; + + t->result = run_fuse_sideload(&vtab, &fd, fd.file_size, fd.block_size); + return NULL; +} + +// How long (in seconds) we wait for the fuse-provided package file to +// appear, before timing out. +#define SDCARD_INSTALL_TIMEOUT 10 + +void* start_sdcard_fuse(const char* path) { + struct token* t = malloc(sizeof(struct token)); + + t->path = path; + pthread_create(&(t->th), NULL, run_sdcard_fuse, t); + + struct stat st; + int i; + for (i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) { + if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) { + if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT-1) { + sleep(1); + continue; + } else { + return NULL; + } + } + } + + // The installation process expects to find the sdcard unmounted. + // Unmount it with MNT_DETACH so that our open file continues to + // work but new references see it as unmounted. + umount2("/sdcard", MNT_DETACH); + + return t; +} + +void finish_sdcard_fuse(void* cookie) { + if (cookie == NULL) return; + struct token* t = (struct token*)cookie; + + // Calling stat() on this magic filename signals the fuse + // filesystem to shut down. + struct stat st; + stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); + + pthread_join(t->th, NULL); + free(t); +} diff --git a/fuse_sdcard_provider.h b/fuse_sdcard_provider.h new file mode 100644 index 000000000..dc2982ca0 --- /dev/null +++ b/fuse_sdcard_provider.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2014 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 __FUSE_SDCARD_PROVIDER_H +#define __FUSE_SDCARD_PROVIDER_H + +void* start_sdcard_fuse(const char* path); +void finish_sdcard_fuse(void* token); + +#endif diff --git a/recovery.cpp b/recovery.cpp index 1c7c0d6a4..d2d85e7c7 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -44,6 +44,8 @@ #include "adb_install.h" extern "C" { #include "minadbd/adb.h" +#include "fuse_sideload.h" +#include "fuse_sdcard_provider.h" } struct selabel_handle *sehandle; @@ -73,7 +75,6 @@ static const char *CACHE_ROOT = "/cache"; static const char *SDCARD_ROOT = "/sdcard"; static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; -static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload"; RecoveryUI* ui = NULL; char* locale = NULL; @@ -439,96 +440,6 @@ erase_volume(const char *volume) { return result; } -static char* -copy_sideloaded_package(const char* original_path) { - if (ensure_path_mounted(original_path) != 0) { - LOGE("Can't mount %s\n", original_path); - return NULL; - } - - if (ensure_path_mounted(SIDELOAD_TEMP_DIR) != 0) { - LOGE("Can't mount %s\n", SIDELOAD_TEMP_DIR); - return NULL; - } - - if (mkdir(SIDELOAD_TEMP_DIR, 0700) != 0) { - if (errno != EEXIST) { - LOGE("Can't mkdir %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno)); - return NULL; - } - } - - // verify that SIDELOAD_TEMP_DIR is exactly what we expect: a - // directory, owned by root, readable and writable only by root. - struct stat st; - if (stat(SIDELOAD_TEMP_DIR, &st) != 0) { - LOGE("failed to stat %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno)); - return NULL; - } - if (!S_ISDIR(st.st_mode)) { - LOGE("%s isn't a directory\n", SIDELOAD_TEMP_DIR); - return NULL; - } - if ((st.st_mode & 0777) != 0700) { - LOGE("%s has perms %o\n", SIDELOAD_TEMP_DIR, st.st_mode); - return NULL; - } - if (st.st_uid != 0) { - LOGE("%s owned by %lu; not root\n", SIDELOAD_TEMP_DIR, st.st_uid); - return NULL; - } - - char copy_path[PATH_MAX]; - strcpy(copy_path, SIDELOAD_TEMP_DIR); - strcat(copy_path, "/package.zip"); - - char* buffer = (char*)malloc(BUFSIZ); - if (buffer == NULL) { - LOGE("Failed to allocate buffer\n"); - return NULL; - } - - size_t read; - FILE* fin = fopen(original_path, "rb"); - if (fin == NULL) { - LOGE("Failed to open %s (%s)\n", original_path, strerror(errno)); - return NULL; - } - FILE* fout = fopen(copy_path, "wb"); - if (fout == NULL) { - LOGE("Failed to open %s (%s)\n", copy_path, strerror(errno)); - return NULL; - } - - while ((read = fread(buffer, 1, BUFSIZ, fin)) > 0) { - if (fwrite(buffer, 1, read, fout) != read) { - LOGE("Short write of %s (%s)\n", copy_path, strerror(errno)); - return NULL; - } - } - - free(buffer); - - if (fclose(fout) != 0) { - LOGE("Failed to close %s (%s)\n", copy_path, strerror(errno)); - return NULL; - } - - if (fclose(fin) != 0) { - LOGE("Failed to close %s (%s)\n", original_path, strerror(errno)); - return NULL; - } - - // "adb push" is happy to overwrite read-only files when it's - // running as root, but we'll try anyway. - if (chmod(copy_path, 0400) != 0) { - LOGE("Failed to chmod %s (%s)\n", copy_path, strerror(errno)); - return NULL; - } - - return strdup(copy_path); -} - static const char** prepend_title(const char* const* headers) { // count the number of lines in our title, plus the @@ -812,17 +723,13 @@ prompt_and_wait(Device* device, int status) { ui->Print("\n-- Install %s ...\n", path); set_sdcard_update_bootloader_message(); - char* copy = copy_sideloaded_package(path); - free(path); - ensure_path_unmounted(SDCARD_ROOT); + void* token = start_sdcard_fuse(path); - int status; - if (copy) { - status = install_package(copy, &wipe_cache, TEMPORARY_INSTALL_FILE, true); - free(copy); - } else { - status = INSTALL_ERROR; - } + int status = install_package(FUSE_SIDELOAD_HOST_PATHNAME, &wipe_cache, + TEMPORARY_INSTALL_FILE, false); + + finish_sdcard_fuse(token); + ensure_path_unmounted(SDCARD_ROOT); if (status == INSTALL_SUCCESS && wipe_cache) { ui->Print("\n-- Wiping cache (at package request)...\n"); @@ -832,6 +739,7 @@ prompt_and_wait(Device* device, int status) { ui->Print("Cache wipe complete.\n"); } } + if (status >= 0) { if (status != INSTALL_SUCCESS) { ui->SetBackground(RecoveryUI::ERROR); -- cgit v1.2.3 From a91ecc59b2753a3dcc93b2619559a980074e77bd Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Mon, 21 Jul 2014 17:40:02 -0700 Subject: Auto create parent directories for rename support Sometimes renames will move a file into a directory that does not yet exist. This will create the parent directories, using the same symlink logic, to ensure that there is a valid destination. Change-Id: Iaa005a12ce800c39f4db20f7c25a2a68cb40a52d --- updater/install.c | 8 +++++--- updater/install.h | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/updater/install.c b/updater/install.c index edc386dc6..5025881d2 100644 --- a/updater/install.c +++ b/updater/install.c @@ -357,8 +357,10 @@ Value* RenameFn(const char* name, State* state, int argc, Expr* argv[]) { name); goto done; } - - if (rename(src_name, dst_name) != 0) { + if (make_parents(dst_name) != 0) { + ErrorAbort(state, "Creating parent of %s() failed, error %s()", + dst_name, strerror(errno)); + } else if (rename(src_name, dst_name) != 0) { ErrorAbort(state, "Rename of %s() to %s() failed, error %s()", src_name, dst_name, strerror(errno)); } else { @@ -642,7 +644,7 @@ static int make_parents(char* name) { *p = '\0'; if (make_parents(name) < 0) return -1; int result = mkdir(name, 0700); - if (result == 0) printf("symlink(): created [%s]\n", name); + if (result == 0) printf("created [%s]\n", name); *p = '/'; if (result == 0 || errno == EEXIST) { // successfully created or already existed; we're done diff --git a/updater/install.h b/updater/install.h index 94f344f8e..659c8b41c 100644 --- a/updater/install.h +++ b/updater/install.h @@ -19,4 +19,6 @@ void RegisterInstallFunctions(); +static int make_parents(char* name); + #endif -- cgit v1.2.3 From eaf33654c1817bd665831a13c5bd0c04daabee02 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 31 Jul 2014 14:59:01 -0700 Subject: only do uncryption on packages in /data If recovery is invoked with a package somewhere other than /data, leave it alone. Change-Id: Ief358b53df467ae24a65e30e7a631da59bf13683 --- uncrypt/uncrypt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c index 24d1ffc2a..bce53dbb9 100644 --- a/uncrypt/uncrypt.c +++ b/uncrypt/uncrypt.c @@ -159,7 +159,7 @@ char* parse_recovery_command_file() while (fgets(temp, sizeof(temp), f)) { printf("read: %s", temp); - if (strncmp(temp, "--update_package=", strlen("--update_package=")) == 0) { + if (strncmp(temp, "--update_package=/data/", strlen("--update_package=/data/")) == 0) { fn = strdup(temp + strlen("--update_package=")); strcpy(temp, "--update_package=@" CACHE_BLOCK_MAP "\n"); } -- cgit v1.2.3 From 78d458c3d279a24a60fda013026b7a5454d01a9b Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 4 Aug 2014 16:44:33 -0700 Subject: Fix length printing + formats Fix wrong argument order. Fix for 32 vs 64 bit. (reported by htc) Change-Id: Ie37a280bed2848199bcc075500e1326e371cd326 --- roots.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roots.cpp b/roots.cpp index 8f9901908..ee140160c 100644 --- a/roots.cpp +++ b/roots.cpp @@ -235,15 +235,15 @@ int format_volume(const char* volume) { result = make_ext4fs(v->blk_device, length, volume, sehandle); } else { /* Has to be f2fs because we checked earlier. */ if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) { - LOGE("format_volume: crypt footer + negative length (%lld) not supported on %s\n", v->fs_type, length); + LOGE("format_volume: crypt footer + negative length (%zd) not supported on %s\n", length, v->fs_type); return -1; } if (length < 0) { - LOGE("format_volume: negative length (%ld) not supported on %s\n", length, v->fs_type); + LOGE("format_volume: negative length (%zd) not supported on %s\n", length, v->fs_type); return -1; } char *num_sectors; - if (asprintf(&num_sectors, "%ld", length / 512) <= 0) { + if (asprintf(&num_sectors, "%zd", length / 512) <= 0) { LOGE("format_volume: failed to create %s command for %s\n", v->fs_type, v->blk_device); return -1; } -- cgit v1.2.3 From 2b5f0e0f767ce51d9605809052ad04fe83d1df83 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 6 Aug 2014 08:25:03 -0700 Subject: remove spurious parens from error message These error messages include empty parens after each string substition. Ill-advised cut and paste, probably. Bug: 16467401 Change-Id: Ib623172d6228354afdcc2e33442cc53a07f0ecbc --- updater/install.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/updater/install.c b/updater/install.c index 5025881d2..198618001 100644 --- a/updater/install.c +++ b/updater/install.c @@ -353,15 +353,14 @@ Value* RenameFn(const char* name, State* state, int argc, Expr* argv[]) { goto done; } if (strlen(dst_name) == 0) { - ErrorAbort(state, "dst_name argument to %s() can't be empty", - name); + ErrorAbort(state, "dst_name argument to %s() can't be empty", name); goto done; } if (make_parents(dst_name) != 0) { - ErrorAbort(state, "Creating parent of %s() failed, error %s()", + ErrorAbort(state, "Creating parent of %s failed, error %s", dst_name, strerror(errno)); } else if (rename(src_name, dst_name) != 0) { - ErrorAbort(state, "Rename of %s() to %s() failed, error %s()", + ErrorAbort(state, "Rename of %s to %s failed, error %s", src_name, dst_name, strerror(errno)); } else { result = dst_name; -- cgit v1.2.3 From ee19387905650cab5da7dd97ada5502cd17ac93d Mon Sep 17 00:00:00 2001 From: Andres Morales Date: Tue, 5 Aug 2014 19:49:09 -0700 Subject: Erase PST partition if its marked to be erased. We need to wipe the challenges on this partition if OEM unlock is enabled, as this is a signal that the user has opted out of factory reset protection. go/factory-reset Bug: 16633064 Change-Id: Icb8f1433bf99ca57813f5b72d5a3dd15fa94a263 --- recovery.cpp | 2 ++ roots.cpp | 37 +++++++++++++++++++++++++++++++++++++ roots.h | 5 +++++ 3 files changed, 44 insertions(+) diff --git a/recovery.cpp b/recovery.cpp index d2d85e7c7..f78d6e46c 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -659,6 +659,7 @@ wipe_data(int confirm, Device* device) { device->WipeData(); erase_volume("/data"); erase_volume("/cache"); + erase_persistent_partition(); ui->Print("Data wipe complete.\n"); } @@ -959,6 +960,7 @@ main(int argc, char **argv) { if (device->WipeData()) status = INSTALL_ERROR; if (erase_volume("/data")) status = INSTALL_ERROR; if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR; + if (erase_persistent_partition() == -1 ) status = INSTALL_ERROR; if (status != INSTALL_SUCCESS) ui->Print("Data wipe failed.\n"); } else if (wipe_cache) { if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR; diff --git a/roots.cpp b/roots.cpp index 8f9901908..61798f495 100644 --- a/roots.cpp +++ b/roots.cpp @@ -39,6 +39,8 @@ static struct fstab *fstab = NULL; extern struct selabel_handle *sehandle; +static const char* PERSISTENT_PATH = "/persistent"; + void load_volume_table() { int i; @@ -264,6 +266,41 @@ int format_volume(const char* volume) { return -1; } +int erase_persistent_partition() { + Volume *v = volume_for_path(PERSISTENT_PATH); + if (v == NULL) { + // most devices won't have /persistent, so this is not an error. + return 0; + } + + int fd = open(v->blk_device, O_RDWR); + uint64_t size = get_file_size(fd); + if (size == 0) { + LOGE("failed to stat size of /persistent\n"); + close(fd); + return -1; + } + + char oem_unlock_enabled; + lseek(fd, size - 1, SEEK_SET); + read(fd, &oem_unlock_enabled, 1); + + if (oem_unlock_enabled) { + if (wipe_block_device(fd, size)) { + LOGE("error wiping /persistent: %s\n", strerror(errno)); + close(fd); + return -1; + } + + lseek(fd, size - 1, SEEK_SET); + write(fd, &oem_unlock_enabled, 1); + } + + close(fd); + + return (int) oem_unlock_enabled; +} + int setup_install_mounts() { if (fstab == NULL) { LOGE("can't set up install mounts: no fstab loaded\n"); diff --git a/roots.h b/roots.h index 230d9ded3..b62a5b13a 100644 --- a/roots.h +++ b/roots.h @@ -46,6 +46,11 @@ int format_volume(const char* volume); // mounted (/tmp and /cache) are mounted. Returns 0 on success. int setup_install_mounts(); +// Conditionally wipes the /persistent partition if it's marked +// to wipe. Returns -1 on failure, 1 if the partition was wiped +// and 0 if the partition was not wiped. +int erase_persistent_partition(); + #ifdef __cplusplus } #endif -- cgit v1.2.3 From 537d34f907a5e984ccad1c88825adc8ae9814834 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 14 Aug 2014 07:59:28 -0700 Subject: change uncrypt to static linking Bug: 17015157 Change-Id: I3c4bdcf4f11d44b617bb731a48413e3707044d1c --- uncrypt/Android.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index 756bc964c..ef3cead34 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -25,4 +25,6 @@ LOCAL_STATIC_LIBRARIES := \ libcutils \ libc +LOCAL_FORCE_STATIC_EXECUTABLE := true + include $(BUILD_EXECUTABLE) -- cgit v1.2.3 From 64be82755cc42b05775265b87a0c64242a72d0dd Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 14 Aug 2014 07:59:28 -0700 Subject: change uncrypt to static linking Bug: 17015157 Change-Id: I3c4bdcf4f11d44b617bb731a48413e3707044d1c --- uncrypt/Android.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index 756bc964c..ef3cead34 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -25,4 +25,6 @@ LOCAL_STATIC_LIBRARIES := \ libcutils \ libc +LOCAL_FORCE_STATIC_EXECUTABLE := true + include $(BUILD_EXECUTABLE) -- cgit v1.2.3 From 1a35a586904cd429fd3a6a6c2de64a16ccdf693d Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 14 Aug 2014 10:29:54 -0700 Subject: revert uncrypt back to dynamic linking, fix libs Bug: 17029174, 17015157 Change-Id: I1d24f3402875dfb972daa6daef0f385baeff84e9 --- uncrypt/Android.mk | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index ef3cead34..8d0a7376e 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -20,11 +20,6 @@ LOCAL_SRC_FILES := uncrypt.c LOCAL_MODULE := uncrypt -LOCAL_STATIC_LIBRARIES := \ - libfs_mgr \ - libcutils \ - libc - -LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_STATIC_LIBRARIES := libfs_mgr libcutils include $(BUILD_EXECUTABLE) -- cgit v1.2.3 From 2efc9d994ce59f9ebfc2290c2adc5d760e8939c2 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 18 Aug 2014 15:55:28 -0700 Subject: clear BCB in misc partition before rebooting Something is leaving behind wipe commands in the BCB area of the /misc partition. We don't know what is doing that. It should always be safe to zero out that area from uncrypt, though (because if uncrypt is running then it's got the command we want in the recovery command file rather than the BCB). Bug: 16715412 Change-Id: Iad01124287f13b80ff71d6371db6371f43c43211 --- uncrypt/uncrypt.c | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c index bce53dbb9..77bfdc207 100644 --- a/uncrypt/uncrypt.c +++ b/uncrypt/uncrypt.c @@ -56,6 +56,8 @@ #define RECOVERY_COMMAND_FILE_TMP "/cache/recovery/command.tmp" #define CACHE_BLOCK_MAP "/cache/recovery/block.map" +static struct fstab* fstab = NULL; + static int write_at_offset(unsigned char* buffer, size_t size, int wfd, off64_t offset) { @@ -101,8 +103,10 @@ void add_block_to_ranges(int** ranges, int* range_alloc, int* range_used, int ne } } -const char* find_block_device(const char* path, int* encryptable, int* encrypted) +static struct fstab* read_fstab() { + fstab = NULL; + // The fstab path is always "/fstab.${ro.hardware}". char fstab_path[PATH_MAX+1] = "/fstab."; if (!property_get("ro.hardware", fstab_path+strlen(fstab_path), "")) { @@ -110,12 +114,17 @@ const char* find_block_device(const char* path, int* encryptable, int* encrypted return NULL; } - struct fstab* fstab = fs_mgr_read_fstab(fstab_path); + fstab = fs_mgr_read_fstab(fstab_path); if (!fstab) { fprintf(stderr, "failed to read %s\n", fstab_path); return NULL; } + return fstab; +} + +const char* find_block_device(const char* path, int* encryptable, int* encrypted) +{ // Look for a volume whose mount point is the prefix of path and // return its block device. Set encrypted if it's currently // encrypted. @@ -302,6 +311,33 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de return 0; } +void wipe_misc() { + int i; + for (i = 0; i < fstab->num_entries; ++i) { + struct fstab_rec* v = &fstab->recs[i]; + if (!v->mount_point) continue; + if (strcmp(v->mount_point, "/misc") == 0) { + int fd = open(v->blk_device, O_RDWR); + uint8_t zeroes[1088]; // sizeof(bootloader_message) from recovery + memset(zeroes, 0, sizeof(zeroes)); + + size_t written = 0; + size_t size = sizeof(zeroes); + while (written < size) { + ssize_t w = write(fd, zeroes, size-written); + if (w < 0 && errno != EINTR) { + fprintf(stderr, "zero write failed: %s\n", strerror(errno)); + return; + } else { + written += w; + } + } + + close(fd); + } + } +} + void reboot_to_recovery() { property_set("sys.powerctl", "reboot,recovery"); sleep(10); @@ -347,6 +383,9 @@ int main(int argc, char** argv) int encryptable; int encrypted; + if (read_fstab() == NULL) { + return 1; + } const char* blk_dev = find_block_device(path, &encryptable, &encrypted); if (blk_dev == NULL) { fprintf(stderr, "failed to find block device for %s\n", path); @@ -376,7 +415,8 @@ int main(int argc, char** argv) } } + wipe_misc(); rename(RECOVERY_COMMAND_FILE_TMP, RECOVERY_COMMAND_FILE); - reboot_to_recovery(); + if (do_reboot) reboot_to_recovery(); return 0; } -- cgit v1.2.3 From bc7ffeda98a861e346c30c771d3258030f7fcf21 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 15 Aug 2014 14:31:52 -0700 Subject: installer for new block OTA system (Cherry-pick back from master.) Bug: 16984795 Change-Id: Ifa3d8345c5e2a0be86fb28faa080ca82592a96b4 --- applypatch/applypatch.c | 6 +- applypatch/applypatch.h | 2 +- applypatch/bspatch.c | 4 +- applypatch/imgpatch.c | 4 +- updater/Android.mk | 1 + updater/blockimg.c | 631 ++++++++++++++++++++++++++++++++++++++++++++++++ updater/blockimg.h | 22 ++ updater/install.c | 2 +- updater/updater.c | 4 + updater/updater.h | 3 + 10 files changed, 669 insertions(+), 10 deletions(-) create mode 100644 updater/blockimg.c create mode 100644 updater/blockimg.h diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 60e9e4a5c..bfb9440e4 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -32,7 +32,7 @@ #include "edify/expr.h" static int LoadPartitionContents(const char* filename, FileContents* file); -static ssize_t FileSink(unsigned char* data, ssize_t len, void* token); +static ssize_t FileSink(const unsigned char* data, ssize_t len, void* token); static int GenerateTarget(FileContents* source_file, const Value* source_patch_value, FileContents* copy_file, @@ -599,7 +599,7 @@ int ShowLicenses() { return 0; } -ssize_t FileSink(unsigned char* data, ssize_t len, void* token) { +ssize_t FileSink(const unsigned char* data, ssize_t len, void* token) { int fd = *(int *)token; ssize_t done = 0; ssize_t wrote; @@ -620,7 +620,7 @@ typedef struct { ssize_t pos; } MemorySinkInfo; -ssize_t MemorySink(unsigned char* data, ssize_t len, void* token) { +ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) { MemorySinkInfo* msi = (MemorySinkInfo*)token; if (msi->size - msi->pos < len) { return -1; diff --git a/applypatch/applypatch.h b/applypatch/applypatch.h index ee54c24ea..edec84812 100644 --- a/applypatch/applypatch.h +++ b/applypatch/applypatch.h @@ -40,7 +40,7 @@ typedef struct _FileContents { // and use it as the source instead. #define CACHE_TEMP_SOURCE "/cache/saved.file" -typedef ssize_t (*SinkFn)(unsigned char*, ssize_t, void*); +typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*); // applypatch.c int ShowLicenses(); diff --git a/applypatch/bspatch.c b/applypatch/bspatch.c index 1dc7ab10b..b34ec2a88 100644 --- a/applypatch/bspatch.c +++ b/applypatch/bspatch.c @@ -112,9 +112,7 @@ int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, printf("short write of output: %d (%s)\n", errno, strerror(errno)); return 1; } - if (ctx) { - SHA_update(ctx, new_data, new_size); - } + if (ctx) SHA_update(ctx, new_data, new_size); free(new_data); return 0; diff --git a/applypatch/imgpatch.c b/applypatch/imgpatch.c index af4d07281..33c448762 100644 --- a/applypatch/imgpatch.c +++ b/applypatch/imgpatch.c @@ -95,7 +95,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, printf("failed to read chunk %d raw data\n", i); return -1; } - SHA_update(ctx, patch->data + pos, data_len); + if (ctx) SHA_update(ctx, patch->data + pos, data_len); if (sink((unsigned char*)patch->data + pos, data_len, token) != data_len) { printf("failed to write chunk %d raw data\n", i); @@ -217,7 +217,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused, (long)have); return -1; } - SHA_update(ctx, temp_data, have); + if (ctx) SHA_update(ctx, temp_data, have); } while (ret != Z_STREAM_END); deflateEnd(&strm); diff --git a/updater/Android.mk b/updater/Android.mk index 99b489029..b183c9221 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -4,6 +4,7 @@ LOCAL_PATH := $(call my-dir) updater_src_files := \ install.c \ + blockimg.c \ updater.c # diff --git a/updater/blockimg.c b/updater/blockimg.c new file mode 100644 index 000000000..c442ab22a --- /dev/null +++ b/updater/blockimg.c @@ -0,0 +1,631 @@ +/* + * Copyright (C) 2014 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "applypatch/applypatch.h" +#include "edify/expr.h" +#include "mincrypt/sha.h" +#include "minzip/DirUtil.h" +#include "updater.h" + +#define BLOCKSIZE 4096 + +// Set this to 1 to interpret 'erase' transfers to mean do a +// BLKDISCARD ioctl. Set to 0 to interpret erase to mean fill the +// region with zeroes. +#define DEBUG_ERASE 0 + +#ifndef BLKDISCARD +#define BLKDISCARD _IO(0x12,119) +#endif + +char* PrintSha1(const uint8_t* digest); + +typedef struct { + int count; + int size; + int pos[0]; +} RangeSet; + +static RangeSet* parse_range(char* text) { + char* save; + int num; + num = strtol(strtok_r(text, ",", &save), NULL, 0); + + RangeSet* out = malloc(sizeof(RangeSet) + num * sizeof(int)); + if (out == NULL) { + fprintf(stderr, "failed to allocate range of %d bytes\n", + sizeof(RangeSet) + num * sizeof(int)); + exit(1); + } + out->count = num / 2; + out->size = 0; + int i; + for (i = 0; i < num; ++i) { + out->pos[i] = strtol(strtok_r(NULL, ",", &save), NULL, 0); + if (i%2) { + out->size += out->pos[i]; + } else { + out->size -= out->pos[i]; + } + } + + return out; +} + +static void readblock(int fd, uint8_t* data, size_t size) { + size_t so_far = 0; + while (so_far < size) { + ssize_t r = read(fd, data+so_far, size-so_far); + if (r < 0 && errno != EINTR) { + fprintf(stderr, "read failed: %s\n", strerror(errno)); + return; + } else { + so_far += r; + } + } +} + +static void writeblock(int fd, const uint8_t* data, size_t size) { + size_t written = 0; + while (written < size) { + ssize_t w = write(fd, data+written, size-written); + if (w < 0 && errno != EINTR) { + fprintf(stderr, "write failed: %s\n", strerror(errno)); + return; + } else { + written += w; + } + } +} + +static void check_lseek(int fd, off_t offset, int whence) { + while (true) { + int ret = lseek(fd, offset, whence); + if (ret < 0) { + if (errno != EINTR) { + fprintf(stderr, "lseek failed: %s\n", strerror(errno)); + exit(1); + } + } else { + break; + } + } +} + +static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) { + // if the buffer's big enough, reuse it. + if (size <= *buffer_alloc) return; + + free(*buffer); + + *buffer = (uint8_t*) malloc(size); + if (*buffer == NULL) { + fprintf(stderr, "failed to allocate %d bytes\n", size); + exit(1); + } + *buffer_alloc = size; +} + +typedef struct { + int fd; + RangeSet* tgt; + int p_block; + size_t p_remain; +} RangeSinkState; + +static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { + RangeSinkState* rss = (RangeSinkState*) token; + + if (rss->p_remain <= 0) { + fprintf(stderr, "range sink write overrun"); + exit(1); + } + + ssize_t written = 0; + while (size > 0) { + size_t write_now = size; + if (rss->p_remain < write_now) write_now = rss->p_remain; + writeblock(rss->fd, data, write_now); + data += write_now; + size -= write_now; + + rss->p_remain -= write_now; + written += write_now; + + if (rss->p_remain == 0) { + // move to the next block + ++rss->p_block; + if (rss->p_block < rss->tgt->count) { + rss->p_remain = (rss->tgt->pos[rss->p_block*2+1] - rss->tgt->pos[rss->p_block*2]) * BLOCKSIZE; + check_lseek(rss->fd, rss->tgt->pos[rss->p_block*2] * BLOCKSIZE, SEEK_SET); + } else { + // we can't write any more; return how many bytes have + // been written so far. + return written; + } + } + } + + return written; +} + +// All of the data for all the 'new' transfers is contained in one +// file in the update package, concatenated together in the order in +// which transfers.list will need it. We want to stream it out of the +// archive (it's compressed) without writing it to a temp file, but we +// can't write each section until it's that transfer's turn to go. +// +// To achieve this, we expand the new data from the archive in a +// background thread, and block that threads 'receive uncompressed +// data' function until the main thread has reached a point where we +// want some new data to be written. We signal the background thread +// with the destination for the data and block the main thread, +// waiting for the background thread to complete writing that section. +// Then it signals the main thread to wake up and goes back to +// blocking waiting for a transfer. +// +// NewThreadInfo is the struct used to pass information back and forth +// between the two threads. When the main thread wants some data +// written, it sets rss to the destination location and signals the +// condition. When the background thread is done writing, it clears +// rss and signals the condition again. + +typedef struct { + ZipArchive* za; + const ZipEntry* entry; + + RangeSinkState* rss; + + pthread_mutex_t mu; + pthread_cond_t cv; +} NewThreadInfo; + +static bool receive_new_data(const unsigned char* data, int size, void* cookie) { + NewThreadInfo* nti = (NewThreadInfo*) cookie; + + while (size > 0) { + // Wait for nti->rss to be non-NULL, indicating some of this + // data is wanted. + pthread_mutex_lock(&nti->mu); + while (nti->rss == NULL) { + pthread_cond_wait(&nti->cv, &nti->mu); + } + pthread_mutex_unlock(&nti->mu); + + // At this point nti->rss is set, and we own it. The main + // thread is waiting for it to disappear from nti. + ssize_t written = RangeSinkWrite(data, size, nti->rss); + data += written; + size -= written; + + if (nti->rss->p_block == nti->rss->tgt->count) { + // we have written all the bytes desired by this rss. + + pthread_mutex_lock(&nti->mu); + nti->rss = NULL; + pthread_cond_broadcast(&nti->cv); + pthread_mutex_unlock(&nti->mu); + } + } + + return true; +} + +static void* unzip_new_data(void* cookie) { + NewThreadInfo* nti = (NewThreadInfo*) cookie; + mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti); + return NULL; +} + +// args: +// - block device (or file) to modify in-place +// - transfer list (blob) +// - new data stream (filename within package.zip) +// - patch stream (filename within package.zip, must be uncompressed) + +Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) { + Value* blockdev_filename; + Value* transfer_list; + Value* new_data_fn; + Value* patch_data_fn; + bool success = false; + + if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list, + &new_data_fn, &patch_data_fn) < 0) { + return NULL; + } + + if (blockdev_filename->type != VAL_STRING) { + ErrorAbort(state, "blockdev_filename argument to %s must be string", name); + goto done; + } + if (transfer_list->type != VAL_BLOB) { + ErrorAbort(state, "transfer_list argument to %s must be blob", name); + goto done; + } + if (new_data_fn->type != VAL_STRING) { + ErrorAbort(state, "new_data_fn argument to %s must be string", name); + goto done; + } + if (patch_data_fn->type != VAL_STRING) { + ErrorAbort(state, "patch_data_fn argument to %s must be string", name); + goto done; + } + + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); + FILE* cmd_pipe = ui->cmd_pipe; + + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + + const ZipEntry* patch_entry = mzFindZipEntry(za, patch_data_fn->data); + if (patch_entry == NULL) { + ErrorAbort(state, "%s(): no file \"%s\" in package", name, patch_data_fn->data); + goto done; + } + + uint8_t* patch_start = ((UpdaterInfo*)(state->cookie))->package_zip_addr + + mzGetZipEntryOffset(patch_entry); + + const ZipEntry* new_entry = mzFindZipEntry(za, new_data_fn->data); + if (new_entry == NULL) { + ErrorAbort(state, "%s(): no file \"%s\" in package", name, new_data_fn->data); + goto done; + } + + // The transfer list is a text file containing commands to + // transfer data from one place to another on the target + // partition. We parse it and execute the commands in order: + // + // zero [rangeset] + // - fill the indicated blocks with zeros + // + // new [rangeset] + // - fill the blocks with data read from the new_data file + // + // bsdiff patchstart patchlen [src rangeset] [tgt rangeset] + // imgdiff patchstart patchlen [src rangeset] [tgt rangeset] + // - read the source blocks, apply a patch, write result to + // target blocks. bsdiff or imgdiff specifies the type of + // patch. + // + // move [src rangeset] [tgt rangeset] + // - copy data from source blocks to target blocks (no patch + // needed; rangesets are the same size) + // + // erase [rangeset] + // - mark the given blocks as empty + // + // The creator of the transfer list will guarantee that no block + // is read (ie, used as the source for a patch or move) after it + // has been written. + // + // Within one command the source and target ranges may overlap so + // in general we need to read the entire source into memory before + // writing anything to the target blocks. + // + // All the patch data is concatenated into one patch_data file in + // the update package. It must be stored uncompressed because we + // memory-map it in directly from the archive. (Since patches are + // already compressed, we lose very little by not compressing + // their concatenation.) + + pthread_t new_data_thread; + NewThreadInfo nti; + nti.za = za; + nti.entry = new_entry; + nti.rss = NULL; + pthread_mutex_init(&nti.mu, NULL); + pthread_cond_init(&nti.cv, NULL); + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_create(&new_data_thread, &attr, unzip_new_data, &nti); + + int i, j; + + char* linesave; + char* wordsave; + + int fd = open(blockdev_filename->data, O_RDWR); + if (fd < 0) { + ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno)); + goto done; + } + + char* line; + char* word; + + line = strtok_r(transfer_list->data, "\n", &linesave); + + // first line in transfer list is the version number; currently + // there's only version 1. + if (strcmp(line, "1") != 0) { + ErrorAbort(state, "unexpected transfer list version [%s]\n", line); + goto done; + } + + // second line in transfer list is the total number of blocks we + // expect to write. + line = strtok_r(NULL, "\n", &linesave); + int total_blocks = strtol(line, NULL, 0); + // shouldn't happen, but avoid divide by zero. + if (total_blocks == 0) ++total_blocks; + int blocks_so_far = 0; + + uint8_t* buffer = NULL; + size_t buffer_alloc = 0; + + // third and subsequent lines are all individual transfer commands. + for (line = strtok_r(NULL, "\n", &linesave); line; + line = strtok_r(NULL, "\n", &linesave)) { + char* style; + style = strtok_r(line, " ", &wordsave); + + if (strcmp("move", style) == 0) { + word = strtok_r(NULL, " ", &wordsave); + RangeSet* src = parse_range(word); + word = strtok_r(NULL, " ", &wordsave); + RangeSet* tgt = parse_range(word); + + printf(" moving %d blocks\n", src->size); + + allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); + size_t p = 0; + for (i = 0; i < src->count; ++i) { + check_lseek(fd, src->pos[i*2] * BLOCKSIZE, SEEK_SET); + size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; + readblock(fd, buffer+p, sz); + p += sz; + } + + p = 0; + for (i = 0; i < tgt->count; ++i) { + check_lseek(fd, tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); + size_t sz = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE; + writeblock(fd, buffer+p, sz); + p += sz; + } + + blocks_so_far += tgt->size; + fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); + fflush(cmd_pipe); + + free(src); + free(tgt); + + } else if (strcmp("zero", style) == 0 || + (DEBUG_ERASE && strcmp("erase", style) == 0)) { + word = strtok_r(NULL, " ", &wordsave); + RangeSet* tgt = parse_range(word); + + printf(" zeroing %d blocks\n", tgt->size); + + allocate(BLOCKSIZE, &buffer, &buffer_alloc); + memset(buffer, 0, BLOCKSIZE); + for (i = 0; i < tgt->count; ++i) { + check_lseek(fd, tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); + for (j = tgt->pos[i*2]; j < tgt->pos[i*2+1]; ++j) { + writeblock(fd, buffer, BLOCKSIZE); + } + } + + if (style[0] == 'z') { // "zero" but not "erase" + blocks_so_far += tgt->size; + fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); + fflush(cmd_pipe); + } + + free(tgt); + } else if (strcmp("new", style) == 0) { + + word = strtok_r(NULL, " ", &wordsave); + RangeSet* tgt = parse_range(word); + + printf(" writing %d blocks of new data\n", tgt->size); + + RangeSinkState rss; + rss.fd = fd; + rss.tgt = tgt; + rss.p_block = 0; + rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; + check_lseek(fd, tgt->pos[0] * BLOCKSIZE, SEEK_SET); + + pthread_mutex_lock(&nti.mu); + nti.rss = &rss; + pthread_cond_broadcast(&nti.cv); + while (nti.rss) { + pthread_cond_wait(&nti.cv, &nti.mu); + } + pthread_mutex_unlock(&nti.mu); + + blocks_so_far += tgt->size; + fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); + fflush(cmd_pipe); + + free(tgt); + + } else if (strcmp("bsdiff", style) == 0 || + strcmp("imgdiff", style) == 0) { + word = strtok_r(NULL, " ", &wordsave); + size_t patch_offset = strtoul(word, NULL, 0); + word = strtok_r(NULL, " ", &wordsave); + size_t patch_len = strtoul(word, NULL, 0); + + word = strtok_r(NULL, " ", &wordsave); + RangeSet* src = parse_range(word); + word = strtok_r(NULL, " ", &wordsave); + RangeSet* tgt = parse_range(word); + + printf(" patching %d blocks to %d\n", src->size, tgt->size); + + // Read the source into memory. + allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); + size_t p = 0; + for (i = 0; i < src->count; ++i) { + check_lseek(fd, src->pos[i*2] * BLOCKSIZE, SEEK_SET); + size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; + readblock(fd, buffer+p, sz); + p += sz; + } + + Value patch_value; + patch_value.type = VAL_BLOB; + patch_value.size = patch_len; + patch_value.data = (char*)(patch_start + patch_offset); + + RangeSinkState rss; + rss.fd = fd; + rss.tgt = tgt; + rss.p_block = 0; + rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; + check_lseek(fd, tgt->pos[0] * BLOCKSIZE, SEEK_SET); + + if (style[0] == 'i') { // imgdiff + ApplyImagePatch(buffer, src->size * BLOCKSIZE, + &patch_value, + &RangeSinkWrite, &rss, NULL, NULL); + } else { + ApplyBSDiffPatch(buffer, src->size * BLOCKSIZE, + &patch_value, 0, + &RangeSinkWrite, &rss, NULL); + } + + // We expect the output of the patcher to fill the tgt ranges exactly. + if (rss.p_block != tgt->count || rss.p_remain != 0) { + fprintf(stderr, "range sink underrun?\n"); + } + + blocks_so_far += tgt->size; + fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); + fflush(cmd_pipe); + + free(src); + free(tgt); + } else if (!DEBUG_ERASE && strcmp("erase", style) == 0) { + struct stat st; + if (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)) { + word = strtok_r(NULL, " ", &wordsave); + RangeSet* tgt = parse_range(word); + + printf(" erasing %d blocks\n", tgt->size); + + for (i = 0; i < tgt->count; ++i) { + uint64_t range[2]; + // offset in bytes + range[0] = tgt->pos[i*2] * BLOCKSIZE; + // len in bytes + range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE; + + if (ioctl(fd, BLKDISCARD, &range) < 0) { + printf(" blkdiscard failed: %s\n", strerror(errno)); + } + } + + free(tgt); + } else { + printf(" ignoring erase (not block device)\n"); + } + } else { + fprintf(stderr, "unknown transfer style \"%s\"\n", style); + exit(1); + } + } + + pthread_join(new_data_thread, NULL); + success = true; + + free(buffer); + printf("wrote %d blocks; expected %d\n", blocks_so_far, total_blocks); + printf("max alloc needed was %zu\n", buffer_alloc); + + done: + FreeValue(blockdev_filename); + FreeValue(transfer_list); + FreeValue(new_data_fn); + FreeValue(patch_data_fn); + return StringValue(success ? strdup("t") : strdup("")); +} + +Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) { + Value* blockdev_filename; + Value* ranges; + const uint8_t* digest = NULL; + if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) { + return NULL; + } + + if (blockdev_filename->type != VAL_STRING) { + ErrorAbort(state, "blockdev_filename argument to %s must be string", name); + goto done; + } + if (ranges->type != VAL_STRING) { + ErrorAbort(state, "ranges argument to %s must be string", name); + goto done; + } + + int fd = open(blockdev_filename->data, O_RDWR); + if (fd < 0) { + ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno)); + goto done; + } + + RangeSet* rs = parse_range(ranges->data); + uint8_t buffer[BLOCKSIZE]; + + SHA_CTX ctx; + SHA_init(&ctx); + + int i, j; + for (i = 0; i < rs->count; ++i) { + check_lseek(fd, rs->pos[i*2] * BLOCKSIZE, SEEK_SET); + for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) { + readblock(fd, buffer, BLOCKSIZE); + SHA_update(&ctx, buffer, BLOCKSIZE); + } + } + digest = SHA_final(&ctx); + close(fd); + + done: + FreeValue(blockdev_filename); + FreeValue(ranges); + if (digest == NULL) { + return StringValue(strdup("")); + } else { + return StringValue(PrintSha1(digest)); + } +} + +void RegisterBlockImageFunctions() { + RegisterFunction("block_image_update", BlockImageUpdateFn); + RegisterFunction("range_sha1", RangeSha1Fn); +} diff --git a/updater/blockimg.h b/updater/blockimg.h new file mode 100644 index 000000000..2f4ad3c04 --- /dev/null +++ b/updater/blockimg.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2014 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_BLOCKIMG_H_ +#define _UPDATER_BLOCKIMG_H_ + +void RegisterBlockImageFunctions(); + +#endif diff --git a/updater/install.c b/updater/install.c index 198618001..cdcdb8fdb 100644 --- a/updater/install.c +++ b/updater/install.c @@ -54,7 +54,7 @@ #endif // Take a sha-1 digest and return it as a newly-allocated hex string. -static char* PrintSha1(const uint8_t* digest) { +char* PrintSha1(const uint8_t* digest) { char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1); int i; const char* alphabet = "0123456789abcdef"; diff --git a/updater/updater.c b/updater/updater.c index b7af3e500..465e1238e 100644 --- a/updater/updater.c +++ b/updater/updater.c @@ -21,6 +21,7 @@ #include "edify/expr.h" #include "updater.h" #include "install.h" +#include "blockimg.h" #include "minzip/Zip.h" #include "minzip/SysUtil.h" @@ -98,6 +99,7 @@ int main(int argc, char** argv) { RegisterBuiltins(); RegisterInstallFunctions(); + RegisterBlockImageFunctions(); RegisterDeviceExtensions(); FinishRegistration(); @@ -127,6 +129,8 @@ int main(int argc, char** argv) { updater_info.cmd_pipe = cmd_pipe; updater_info.package_zip = &za; updater_info.version = atoi(version); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; State state; state.cookie = &updater_info; diff --git a/updater/updater.h b/updater/updater.h index d2e901141..d1dfdd05e 100644 --- a/updater/updater.h +++ b/updater/updater.h @@ -27,6 +27,9 @@ typedef struct { FILE* cmd_pipe; ZipArchive* package_zip; int version; + + uint8_t* package_zip_addr; + size_t package_zip_len; } UpdaterInfo; extern struct selabel_handle *sehandle; -- cgit v1.2.3 From 1d5d6098f4a470bc8e56ae8914180041815e6e22 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 21 Aug 2014 10:47:24 -0700 Subject: fix two bugs in block image updater The computation of file offsets was overflowing for partitions larger than 2 GB. The parsing of the transfer file could fail at the end if the data happened to not be properly null-terminated. Bug: 16984795 Change-Id: I3ce6eb3e54ab7b55aa9bbed252da5a7eacd3317a --- updater/blockimg.c | 50 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/updater/blockimg.c b/updater/blockimg.c index c442ab22a..6d412d5d5 100644 --- a/updater/blockimg.c +++ b/updater/blockimg.c @@ -61,7 +61,7 @@ static RangeSet* parse_range(char* text) { RangeSet* out = malloc(sizeof(RangeSet) + num * sizeof(int)); if (out == NULL) { - fprintf(stderr, "failed to allocate range of %d bytes\n", + fprintf(stderr, "failed to allocate range of %lu bytes\n", sizeof(RangeSet) + num * sizeof(int)); exit(1); } @@ -108,7 +108,7 @@ static void writeblock(int fd, const uint8_t* data, size_t size) { static void check_lseek(int fd, off_t offset, int whence) { while (true) { - int ret = lseek(fd, offset, whence); + off_t ret = lseek(fd, offset, whence); if (ret < 0) { if (errno != EINTR) { fprintf(stderr, "lseek failed: %s\n", strerror(errno)); @@ -128,7 +128,7 @@ static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) { *buffer = (uint8_t*) malloc(size); if (*buffer == NULL) { - fprintf(stderr, "failed to allocate %d bytes\n", size); + fprintf(stderr, "failed to allocate %zu bytes\n", size); exit(1); } *buffer_alloc = size; @@ -165,7 +165,7 @@ static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { ++rss->p_block; if (rss->p_block < rss->tgt->count) { rss->p_remain = (rss->tgt->pos[rss->p_block*2+1] - rss->tgt->pos[rss->p_block*2]) * BLOCKSIZE; - check_lseek(rss->fd, rss->tgt->pos[rss->p_block*2] * BLOCKSIZE, SEEK_SET); + check_lseek(rss->fd, rss->tgt->pos[rss->p_block*2] * (off_t)BLOCKSIZE, SEEK_SET); } else { // we can't write any more; return how many bytes have // been written so far. @@ -253,12 +253,13 @@ static void* unzip_new_data(void* cookie) { Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) { Value* blockdev_filename; - Value* transfer_list; + Value* transfer_list_value; + char* transfer_list = NULL; Value* new_data_fn; Value* patch_data_fn; bool success = false; - if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list, + if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value, &new_data_fn, &patch_data_fn) < 0) { return NULL; } @@ -267,7 +268,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] ErrorAbort(state, "blockdev_filename argument to %s must be string", name); goto done; } - if (transfer_list->type != VAL_BLOB) { + if (transfer_list_value->type != VAL_BLOB) { ErrorAbort(state, "transfer_list argument to %s must be blob", name); goto done; } @@ -364,7 +365,19 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] char* line; char* word; - line = strtok_r(transfer_list->data, "\n", &linesave); + // The data in transfer_list_value is not necessarily + // null-terminated, so we need to copy it to a new buffer and add + // the null that strtok_r will need. + transfer_list = malloc(transfer_list_value->size+1); + if (transfer_list == NULL) { + fprintf(stderr, "failed to allocate %zd bytes for transfer list\n", + transfer_list_value->size+1); + exit(1); + } + memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size); + transfer_list[transfer_list_value->size] = '\0'; + + line = strtok_r(transfer_list, "\n", &linesave); // first line in transfer list is the version number; currently // there's only version 1. @@ -401,7 +414,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); size_t p = 0; for (i = 0; i < src->count; ++i) { - check_lseek(fd, src->pos[i*2] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, src->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; readblock(fd, buffer+p, sz); p += sz; @@ -409,7 +422,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] p = 0; for (i = 0; i < tgt->count; ++i) { - check_lseek(fd, tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, tgt->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); size_t sz = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE; writeblock(fd, buffer+p, sz); p += sz; @@ -432,7 +445,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] allocate(BLOCKSIZE, &buffer, &buffer_alloc); memset(buffer, 0, BLOCKSIZE); for (i = 0; i < tgt->count; ++i) { - check_lseek(fd, tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, tgt->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); for (j = tgt->pos[i*2]; j < tgt->pos[i*2+1]; ++j) { writeblock(fd, buffer, BLOCKSIZE); } @@ -457,7 +470,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] rss.tgt = tgt; rss.p_block = 0; rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; - check_lseek(fd, tgt->pos[0] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, tgt->pos[0] * (off_t)BLOCKSIZE, SEEK_SET); pthread_mutex_lock(&nti.mu); nti.rss = &rss; @@ -491,7 +504,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); size_t p = 0; for (i = 0; i < src->count; ++i) { - check_lseek(fd, src->pos[i*2] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, src->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; readblock(fd, buffer+p, sz); p += sz; @@ -507,7 +520,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] rss.tgt = tgt; rss.p_block = 0; rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; - check_lseek(fd, tgt->pos[0] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, tgt->pos[0] * (off_t)BLOCKSIZE, SEEK_SET); if (style[0] == 'i') { // imgdiff ApplyImagePatch(buffer, src->size * BLOCKSIZE, @@ -541,9 +554,9 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] for (i = 0; i < tgt->count; ++i) { uint64_t range[2]; // offset in bytes - range[0] = tgt->pos[i*2] * BLOCKSIZE; + range[0] = tgt->pos[i*2] * (uint64_t)BLOCKSIZE; // len in bytes - range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE; + range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * (uint64_t)BLOCKSIZE; if (ioctl(fd, BLKDISCARD, &range) < 0) { printf(" blkdiscard failed: %s\n", strerror(errno)); @@ -568,8 +581,9 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] printf("max alloc needed was %zu\n", buffer_alloc); done: + free(transfer_list); FreeValue(blockdev_filename); - FreeValue(transfer_list); + FreeValue(transfer_list_value); FreeValue(new_data_fn); FreeValue(patch_data_fn); return StringValue(success ? strdup("t") : strdup("")); @@ -606,7 +620,7 @@ Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) { int i, j; for (i = 0; i < rs->count; ++i) { - check_lseek(fd, rs->pos[i*2] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, rs->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) { readblock(fd, buffer, BLOCKSIZE); SHA_update(&ctx, buffer, BLOCKSIZE); -- cgit v1.2.3 From f2127b6435eedd2c2c57a480a0ed049fee014406 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 21 Aug 2014 10:47:24 -0700 Subject: fix two bugs in block image updater The computation of file offsets was overflowing for partitions larger than 2 GB. The parsing of the transfer file could fail at the end if the data happened to not be properly null-terminated. Bug: 16984795 Change-Id: I3ce6eb3e54ab7b55aa9bbed252da5a7eacd3317a --- updater/blockimg.c | 50 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/updater/blockimg.c b/updater/blockimg.c index c442ab22a..6d412d5d5 100644 --- a/updater/blockimg.c +++ b/updater/blockimg.c @@ -61,7 +61,7 @@ static RangeSet* parse_range(char* text) { RangeSet* out = malloc(sizeof(RangeSet) + num * sizeof(int)); if (out == NULL) { - fprintf(stderr, "failed to allocate range of %d bytes\n", + fprintf(stderr, "failed to allocate range of %lu bytes\n", sizeof(RangeSet) + num * sizeof(int)); exit(1); } @@ -108,7 +108,7 @@ static void writeblock(int fd, const uint8_t* data, size_t size) { static void check_lseek(int fd, off_t offset, int whence) { while (true) { - int ret = lseek(fd, offset, whence); + off_t ret = lseek(fd, offset, whence); if (ret < 0) { if (errno != EINTR) { fprintf(stderr, "lseek failed: %s\n", strerror(errno)); @@ -128,7 +128,7 @@ static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) { *buffer = (uint8_t*) malloc(size); if (*buffer == NULL) { - fprintf(stderr, "failed to allocate %d bytes\n", size); + fprintf(stderr, "failed to allocate %zu bytes\n", size); exit(1); } *buffer_alloc = size; @@ -165,7 +165,7 @@ static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { ++rss->p_block; if (rss->p_block < rss->tgt->count) { rss->p_remain = (rss->tgt->pos[rss->p_block*2+1] - rss->tgt->pos[rss->p_block*2]) * BLOCKSIZE; - check_lseek(rss->fd, rss->tgt->pos[rss->p_block*2] * BLOCKSIZE, SEEK_SET); + check_lseek(rss->fd, rss->tgt->pos[rss->p_block*2] * (off_t)BLOCKSIZE, SEEK_SET); } else { // we can't write any more; return how many bytes have // been written so far. @@ -253,12 +253,13 @@ static void* unzip_new_data(void* cookie) { Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) { Value* blockdev_filename; - Value* transfer_list; + Value* transfer_list_value; + char* transfer_list = NULL; Value* new_data_fn; Value* patch_data_fn; bool success = false; - if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list, + if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value, &new_data_fn, &patch_data_fn) < 0) { return NULL; } @@ -267,7 +268,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] ErrorAbort(state, "blockdev_filename argument to %s must be string", name); goto done; } - if (transfer_list->type != VAL_BLOB) { + if (transfer_list_value->type != VAL_BLOB) { ErrorAbort(state, "transfer_list argument to %s must be blob", name); goto done; } @@ -364,7 +365,19 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] char* line; char* word; - line = strtok_r(transfer_list->data, "\n", &linesave); + // The data in transfer_list_value is not necessarily + // null-terminated, so we need to copy it to a new buffer and add + // the null that strtok_r will need. + transfer_list = malloc(transfer_list_value->size+1); + if (transfer_list == NULL) { + fprintf(stderr, "failed to allocate %zd bytes for transfer list\n", + transfer_list_value->size+1); + exit(1); + } + memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size); + transfer_list[transfer_list_value->size] = '\0'; + + line = strtok_r(transfer_list, "\n", &linesave); // first line in transfer list is the version number; currently // there's only version 1. @@ -401,7 +414,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); size_t p = 0; for (i = 0; i < src->count; ++i) { - check_lseek(fd, src->pos[i*2] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, src->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; readblock(fd, buffer+p, sz); p += sz; @@ -409,7 +422,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] p = 0; for (i = 0; i < tgt->count; ++i) { - check_lseek(fd, tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, tgt->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); size_t sz = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE; writeblock(fd, buffer+p, sz); p += sz; @@ -432,7 +445,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] allocate(BLOCKSIZE, &buffer, &buffer_alloc); memset(buffer, 0, BLOCKSIZE); for (i = 0; i < tgt->count; ++i) { - check_lseek(fd, tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, tgt->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); for (j = tgt->pos[i*2]; j < tgt->pos[i*2+1]; ++j) { writeblock(fd, buffer, BLOCKSIZE); } @@ -457,7 +470,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] rss.tgt = tgt; rss.p_block = 0; rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; - check_lseek(fd, tgt->pos[0] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, tgt->pos[0] * (off_t)BLOCKSIZE, SEEK_SET); pthread_mutex_lock(&nti.mu); nti.rss = &rss; @@ -491,7 +504,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); size_t p = 0; for (i = 0; i < src->count; ++i) { - check_lseek(fd, src->pos[i*2] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, src->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; readblock(fd, buffer+p, sz); p += sz; @@ -507,7 +520,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] rss.tgt = tgt; rss.p_block = 0; rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; - check_lseek(fd, tgt->pos[0] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, tgt->pos[0] * (off_t)BLOCKSIZE, SEEK_SET); if (style[0] == 'i') { // imgdiff ApplyImagePatch(buffer, src->size * BLOCKSIZE, @@ -541,9 +554,9 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] for (i = 0; i < tgt->count; ++i) { uint64_t range[2]; // offset in bytes - range[0] = tgt->pos[i*2] * BLOCKSIZE; + range[0] = tgt->pos[i*2] * (uint64_t)BLOCKSIZE; // len in bytes - range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE; + range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * (uint64_t)BLOCKSIZE; if (ioctl(fd, BLKDISCARD, &range) < 0) { printf(" blkdiscard failed: %s\n", strerror(errno)); @@ -568,8 +581,9 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] printf("max alloc needed was %zu\n", buffer_alloc); done: + free(transfer_list); FreeValue(blockdev_filename); - FreeValue(transfer_list); + FreeValue(transfer_list_value); FreeValue(new_data_fn); FreeValue(patch_data_fn); return StringValue(success ? strdup("t") : strdup("")); @@ -606,7 +620,7 @@ Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) { int i, j; for (i = 0; i < rs->count; ++i) { - check_lseek(fd, rs->pos[i*2] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, rs->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) { readblock(fd, buffer, BLOCKSIZE); SHA_update(&ctx, buffer, BLOCKSIZE); -- cgit v1.2.3 From f449db2f30235a0c2fef4bc7bc41776e271a60a0 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 26 Aug 2014 09:15:08 -0700 Subject: open misc device in write-only mode Opening the misc block device in read-write mode runs afoul of SELinux, which keeps the wipe code from working. Fix. Also change various things to log to logcat so we can see them happening, for future debugging. Bug: 16715412 Change-Id: Ia14066f0a371cd605fcb544547b58a41acca70b9 --- uncrypt/Android.mk | 2 +- uncrypt/uncrypt.c | 44 ++++++++++++++++++++++++++------------------ 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index 8d0a7376e..878d2757e 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -20,6 +20,6 @@ LOCAL_SRC_FILES := uncrypt.c LOCAL_MODULE := uncrypt -LOCAL_STATIC_LIBRARIES := libfs_mgr libcutils +LOCAL_STATIC_LIBRARIES := libfs_mgr liblog libcutils include $(BUILD_EXECUTABLE) diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c index 77bfdc207..07e5ae665 100644 --- a/uncrypt/uncrypt.c +++ b/uncrypt/uncrypt.c @@ -48,6 +48,8 @@ #include #include +#define LOG_TAG "uncrypt" +#include #include #include @@ -66,7 +68,7 @@ static int write_at_offset(unsigned char* buffer, size_t size, while (written < size) { ssize_t wrote = write(wfd, buffer + written, size - written); if (wrote < 0) { - fprintf(stderr, "error writing offset %lld: %s\n", offset, strerror(errno)); + ALOGE("error writing offset %lld: %s\n", offset, strerror(errno)); return -1; } written += wrote; @@ -110,13 +112,13 @@ static struct fstab* read_fstab() // The fstab path is always "/fstab.${ro.hardware}". char fstab_path[PATH_MAX+1] = "/fstab."; if (!property_get("ro.hardware", fstab_path+strlen(fstab_path), "")) { - fprintf(stderr, "failed to get ro.hardware\n"); + ALOGE("failed to get ro.hardware\n"); return NULL; } fstab = fs_mgr_read_fstab(fstab_path); if (!fstab) { - fprintf(stderr, "failed to read %s\n", fstab_path); + ALOGE("failed to read %s\n", fstab_path); return NULL; } @@ -194,14 +196,14 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de ret = stat(path, &sb); if (ret != 0) { - fprintf(stderr, "failed to stat %s\n", path); + ALOGE("failed to stat %s\n", path); return -1; } - printf(" block size: %ld bytes\n", (long)sb.st_blksize); + ALOGI(" block size: %ld bytes\n", (long)sb.st_blksize); int blocks = ((sb.st_size-1) / sb.st_blksize) + 1; - printf(" file size: %lld bytes, %d blocks\n", (long long)sb.st_size, blocks); + ALOGI(" file size: %lld bytes, %d blocks\n", (long long)sb.st_size, blocks); int* ranges; int range_alloc = 1; @@ -225,7 +227,7 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de int fd = open(path, O_RDONLY); if (fd < 0) { - fprintf(stderr, "failed to open fd for reading: %s\n", strerror(errno)); + ALOGE("failed to open fd for reading: %s\n", strerror(errno)); return -1; } fsync(fd); @@ -234,7 +236,7 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de if (encrypted) { wfd = open(blk_dev, O_WRONLY); if (wfd < 0) { - fprintf(stderr, "failed to open fd for writing: %s\n", strerror(errno)); + ALOGE("failed to open fd for writing: %s\n", strerror(errno)); return -1; } } @@ -245,7 +247,7 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de int block = head_block; ret = ioctl(fd, FIBMAP, &block); if (ret != 0) { - fprintf(stderr, "failed to find block %d\n", head_block); + ALOGE("failed to find block %d\n", head_block); return -1; } add_block_to_ranges(&ranges, &range_alloc, &range_used, block); @@ -264,7 +266,7 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de while (so_far < sb.st_blksize && pos < sb.st_size) { ssize_t this_read = read(fd, buffers[tail] + so_far, sb.st_blksize - so_far); if (this_read < 0) { - fprintf(stderr, "failed to read: %s\n", strerror(errno)); + ALOGE("failed to read: %s\n", strerror(errno)); return -1; } so_far += this_read; @@ -284,7 +286,7 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de int block = head_block; ret = ioctl(fd, FIBMAP, &block); if (ret != 0) { - fprintf(stderr, "failed to find block %d\n", head_block); + ALOGE("failed to find block %d\n", head_block); return -1; } add_block_to_ranges(&ranges, &range_alloc, &range_used, block); @@ -312,12 +314,13 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de } void wipe_misc() { + ALOGI("removing old commands from misc"); int i; for (i = 0; i < fstab->num_entries; ++i) { struct fstab_rec* v = &fstab->recs[i]; if (!v->mount_point) continue; if (strcmp(v->mount_point, "/misc") == 0) { - int fd = open(v->blk_device, O_RDWR); + int fd = open(v->blk_device, O_WRONLY); uint8_t zeroes[1088]; // sizeof(bootloader_message) from recovery memset(zeroes, 0, sizeof(zeroes)); @@ -326,7 +329,7 @@ void wipe_misc() { while (written < size) { ssize_t w = write(fd, zeroes, size-written); if (w < 0 && errno != EINTR) { - fprintf(stderr, "zero write failed: %s\n", strerror(errno)); + ALOGE("zero write failed: %s\n", strerror(errno)); return; } else { written += w; @@ -339,8 +342,10 @@ void wipe_misc() { } void reboot_to_recovery() { + ALOGI("rebooting to recovery"); property_set("sys.powerctl", "reboot,recovery"); sleep(10); + ALOGE("reboot didn't succeed?"); } int main(int argc, char** argv) @@ -366,18 +371,20 @@ int main(int argc, char** argv) // if we're rebooting to recovery without a package (say, // to wipe data), then we don't need to do anything before // going to recovery. - fprintf(stderr, "no recovery command file or no update package arg"); + ALOGI("no recovery command file or no update package arg"); reboot_to_recovery(); return 1; } map_file = CACHE_BLOCK_MAP; } + ALOGI("update package is %s", input_path); + // Turn the name of the file we're supposed to convert into an // absolute path, so we can find what filesystem it's on. char path[PATH_MAX+1]; if (realpath(input_path, path) == NULL) { - fprintf(stderr, "failed to convert %s to absolute path: %s\n", input_path, strerror(errno)); + ALOGE("failed to convert %s to absolute path: %s", input_path, strerror(errno)); return 1; } @@ -388,15 +395,15 @@ int main(int argc, char** argv) } const char* blk_dev = find_block_device(path, &encryptable, &encrypted); if (blk_dev == NULL) { - fprintf(stderr, "failed to find block device for %s\n", path); + ALOGE("failed to find block device for %s", path); return 1; } // If the filesystem it's on isn't encrypted, we only produce the // block map, we don't rewrite the file contents (it would be // pointless to do so). - printf("encryptable: %s\n", encryptable ? "yes" : "no"); - printf(" encrypted: %s\n", encrypted ? "yes" : "no"); + ALOGI("encryptable: %s\n", encryptable ? "yes" : "no"); + ALOGI(" encrypted: %s\n", encrypted ? "yes" : "no"); if (!encryptable) { // If the file is on a filesystem that doesn't support @@ -410,6 +417,7 @@ int main(int argc, char** argv) unlink(RECOVERY_COMMAND_FILE_TMP); } else { + ALOGI("writing block map %s", map_file); if (produce_block_map(path, map_file, blk_dev, encrypted) != 0) { return 1; } -- cgit v1.2.3 From 5f875bf57739bf0b5c2d182173513233bdb71ab8 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 22 Aug 2014 14:53:43 -0700 Subject: remove code for original block OTA mechanism Superseded by newer code. Bug: 16984795 Change-Id: I842299f6a02af7ccf51ef2ca174d813ca53deef1 --- updater/Android.mk | 2 - updater/install.c | 213 ++--------------------------------------------------- 2 files changed, 6 insertions(+), 209 deletions(-) diff --git a/updater/Android.mk b/updater/Android.mk index b183c9221..a3a900a80 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -36,8 +36,6 @@ LOCAL_STATIC_LIBRARIES += libcutils liblog libstdc++ libc LOCAL_STATIC_LIBRARIES += libselinux LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. -LOCAL_STATIC_LIBRARIES += libsyspatch libxz libxdelta3 - # Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function # named "Register_()". Here we emit a little C function that # gets #included by updater.c. It calls all those registration diff --git a/updater/install.c b/updater/install.c index cdcdb8fdb..dad0d08c9 100644 --- a/updater/install.c +++ b/updater/install.c @@ -45,7 +45,6 @@ #include "mtdutils/mounts.h" #include "mtdutils/mtdutils.h" #include "updater.h" -#include "syspatch.h" #include "install.h" #ifdef USE_EXT4 @@ -464,68 +463,6 @@ Value* PackageExtractDirFn(const char* name, State* state, } -DontCareMap* ReadDontCareMapFromZip(ZipArchive* za, const char* path) { - const char* name = "ReadDontCareMapFromZip"; - - const ZipEntry* entry = mzFindZipEntry(za, path); - if (entry == NULL) { - printf("%s: no %s in package\n", name, path); - return NULL; - } - - size_t map_size = mzGetZipEntryUncompLen(entry); - char* map_data = malloc(map_size); - if (map_data == NULL) { - printf("%s: failed to allocate %zu bytes for %s\n", - name, map_size, path); - return NULL; - } - - if (!mzExtractZipEntryToBuffer(za, entry, (unsigned char*) map_data)) { - printf("%s: failed to read %s\n", name, path); - return NULL; - } - - char* p = map_data; - DontCareMap* map = (DontCareMap*) malloc(sizeof(DontCareMap)); - - map->block_size = strtoul(p, &p, 0); - if (map->block_size != 4096) { - printf("%s: unexpected block size %zu\n", name, map->block_size); - return NULL; - } - - map->region_count = strtoul(p, &p, 0); - map->regions = (int*) malloc(map->region_count * sizeof(int)); - map->total_blocks = 0; - - int i; - for (i = 0; i < map->region_count; ++i) { - map->regions[i] = strtoul(p, &p, 0); - map->total_blocks += map->regions[i]; - } - - return map; -} - -static FILE* mapwrite_cmd_pipe; - -static void progress_cb(long done, long total) { - if (total > 0) { - double frac = (double)done / total; - fprintf(mapwrite_cmd_pipe, "set_progress %f\n", frac); - fflush(mapwrite_cmd_pipe); - } -} - - - -bool MapWriter(const unsigned char* data, int dataLen, void* cookie) { - return write_with_map(data, dataLen, (MapState*) cookie, progress_cb) == dataLen; -} - -// package_extract_file(package_path, destination_path, map_path) -// or // package_extract_file(package_path, destination_path) // or // package_extract_file(package_path) @@ -533,33 +470,22 @@ bool MapWriter(const unsigned char* data, int dataLen, void* cookie) { // function (the char* returned is actually a FileContents*). Value* PackageExtractFileFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc < 1 || argc > 3) { - return ErrorAbort(state, "%s() expects 1 or 2 or 3 args, got %d", + if (argc < 1 || argc > 2) { + return ErrorAbort(state, "%s() expects 1 or 2 args, got %d", name, argc); } bool success = false; UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); - mapwrite_cmd_pipe = ui->cmd_pipe; - if (argc >= 2) { - // The two-argument version extracts to a file; the three-arg - // version extracts to a file, skipping over regions in a - // don't care map. + if (argc == 2) { + // The two-argument version extracts to a file. ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; char* zip_path; char* dest_path; - char* map_path = NULL; - DontCareMap* map = NULL; - if (argc == 2) { - if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; - } else { - if (ReadArgs(state, argv, 3, &zip_path, &dest_path, &map_path) < 0) return NULL; - map = ReadDontCareMapFromZip(za, map_path); - if (map == NULL) goto done2; - } + if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; const ZipEntry* entry = mzFindZipEntry(za, zip_path); if (entry == NULL) { @@ -573,26 +499,12 @@ Value* PackageExtractFileFn(const char* name, State* state, name, dest_path, strerror(errno)); goto done2; } - if (map) { - MapState state; - state.map = map; - state.cr = 0; - state.so_far = 0; - state.f = f; - success = mzProcessZipEntryContents(za, entry, MapWriter, &state); - } else { - success = mzExtractZipEntryToFile(za, entry, fileno(f)); - } + success = mzExtractZipEntryToFile(za, entry, fileno(f)); fclose(f); done2: free(zip_path); free(dest_path); - free(map_path); - if (map) { - free(map->regions); - free(map); - } return StringValue(strdup(success ? "t" : "")); } else { // The one-argument version returns the contents of the file @@ -1192,118 +1104,6 @@ Value* ApplyPatchSpaceFn(const char* name, State* state, return StringValue(strdup(CacheSizeCheck(bytes) ? "" : "t")); } -bool CheckMappedFileSha1(FILE* f, DontCareMap* map, uint8_t* intended_digest) { - MapState state; - - state.f = f; - state.so_far = 0; - state.cr = 0; - state.map = map; - - SHA_CTX ctx; - SHA_init(&ctx); - - unsigned char buffer[32173]; - size_t bytes_read; - - while ((bytes_read = read_with_map(buffer, sizeof(buffer), &state)) > 0) { - SHA_update(&ctx, buffer, bytes_read); - } - const uint8_t* digest = SHA_final(&ctx); - - return memcmp(digest, intended_digest, SHA_DIGEST_SIZE) == 0; -} - - -// syspatch(file, tgt_mapfile, tgt_sha1, init_mapfile, init_sha1, patch) - -Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 6) { - return ErrorAbort(state, "%s(): expected 6 args, got %d", name, argc); - } - - char* filename; - char* target_mapfilename; - char* target_sha1; - char* init_mapfilename; - char* init_sha1; - char* patch_filename; - uint8_t target_digest[SHA_DIGEST_SIZE]; - uint8_t init_digest[SHA_DIGEST_SIZE]; - - if (ReadArgs(state, argv, 6, &filename, - &target_mapfilename, &target_sha1, - &init_mapfilename, &init_sha1, &patch_filename) < 0) { - return NULL; - } - - UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); - mapwrite_cmd_pipe = ui->cmd_pipe; - - if (ParseSha1(target_sha1, target_digest) != 0) { - printf("%s(): failed to parse '%s' as target SHA-1", name, target_sha1); - memset(target_digest, 0, SHA_DIGEST_SIZE); - } - if (ParseSha1(init_sha1, init_digest) != 0) { - printf("%s(): failed to parse '%s' as init SHA-1", name, init_sha1); - memset(init_digest, 0, SHA_DIGEST_SIZE); - } - - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; - FILE* src = fopen(filename, "r"); - - DontCareMap* init_map = ReadDontCareMapFromZip(za, init_mapfilename); - if (init_map == NULL) return ErrorAbort(state, "%s(): failed to read init map\n", name); - DontCareMap* target_map = ReadDontCareMapFromZip(za, target_mapfilename); - if (target_map == NULL) return ErrorAbort(state, "%s(): failed to read target map\n", name); - - if (CheckMappedFileSha1(src, init_map, init_digest)) { - // If the partition contents match the init_digest, then we need to apply the patch. - - rewind(src); - - const ZipEntry* entry = mzFindZipEntry(za, patch_filename); - if (entry == NULL) { - return ErrorAbort(state, "%s(): no %s in package\n", name, patch_filename); - } - - unsigned char* patch_data; - size_t patch_len; - if (!mzGetStoredEntry(za, entry, &patch_data, &patch_len)) { - return ErrorAbort(state, "%s(): failed to get %s entry\n", name, patch_filename); - } - - FILE* tgt = fopen(filename, "r+"); - - int ret = syspatch(src, init_map, patch_data, patch_len, tgt, target_map, progress_cb); - - fclose(src); - fclose(tgt); - - if (ret != 0) { - return ErrorAbort(state, "%s(): patching failed\n", name); - } - } else { - rewind(src); - if (CheckMappedFileSha1(src, target_map, target_digest)) { - // If the partition contents match the target already, we - // don't need to do anything. - printf("%s: output is already target\n", name); - } else { - return ErrorAbort(state, "%s(): %s in unknown state\n", name, filename); - } - } - - done: - free(target_sha1); - free(target_mapfilename); - free(init_sha1); - free(init_mapfilename); - free(patch_filename); - return StringValue(filename); - -} - // apply_patch(file, size, init_sha1, tgt_sha1, patch) Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { @@ -1738,7 +1538,6 @@ void RegisterInstallFunctions() { RegisterFunction("apply_patch_space", ApplyPatchSpaceFn); RegisterFunction("wipe_block_device", WipeBlockDeviceFn); - RegisterFunction("syspatch", SysPatchFn); RegisterFunction("read_file", ReadFileFn); RegisterFunction("sha1_check", Sha1CheckFn); -- cgit v1.2.3 From 8328922ff040280007da0aaaf8b567581231d5ed Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 3 Sep 2014 12:41:06 -0700 Subject: use lseek64 instead of lseek Otherwise, overflow problems can occur with images larger than 2G since the offsets will overflow a 32-bit off_t. Change-Id: I05951a38ebeae83ad2cb938594e8d8adb323e2aa Signed-off-by: Andrew Boie --- updater/blockimg.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/updater/blockimg.c b/updater/blockimg.c index 6d412d5d5..5c0c3a727 100644 --- a/updater/blockimg.c +++ b/updater/blockimg.c @@ -106,12 +106,12 @@ static void writeblock(int fd, const uint8_t* data, size_t size) { } } -static void check_lseek(int fd, off_t offset, int whence) { +static void check_lseek(int fd, off64_t offset, int whence) { while (true) { - off_t ret = lseek(fd, offset, whence); + off64_t ret = lseek64(fd, offset, whence); if (ret < 0) { if (errno != EINTR) { - fprintf(stderr, "lseek failed: %s\n", strerror(errno)); + fprintf(stderr, "lseek64 failed: %s\n", strerror(errno)); exit(1); } } else { @@ -165,7 +165,7 @@ static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { ++rss->p_block; if (rss->p_block < rss->tgt->count) { rss->p_remain = (rss->tgt->pos[rss->p_block*2+1] - rss->tgt->pos[rss->p_block*2]) * BLOCKSIZE; - check_lseek(rss->fd, rss->tgt->pos[rss->p_block*2] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE, SEEK_SET); } else { // we can't write any more; return how many bytes have // been written so far. @@ -414,7 +414,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); size_t p = 0; for (i = 0; i < src->count; ++i) { - check_lseek(fd, src->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET); size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; readblock(fd, buffer+p, sz); p += sz; @@ -422,7 +422,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] p = 0; for (i = 0; i < tgt->count; ++i) { - check_lseek(fd, tgt->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); size_t sz = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE; writeblock(fd, buffer+p, sz); p += sz; @@ -445,7 +445,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] allocate(BLOCKSIZE, &buffer, &buffer_alloc); memset(buffer, 0, BLOCKSIZE); for (i = 0; i < tgt->count; ++i) { - check_lseek(fd, tgt->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); for (j = tgt->pos[i*2]; j < tgt->pos[i*2+1]; ++j) { writeblock(fd, buffer, BLOCKSIZE); } @@ -470,7 +470,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] rss.tgt = tgt; rss.p_block = 0; rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; - check_lseek(fd, tgt->pos[0] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET); pthread_mutex_lock(&nti.mu); nti.rss = &rss; @@ -504,7 +504,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); size_t p = 0; for (i = 0; i < src->count; ++i) { - check_lseek(fd, src->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET); size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; readblock(fd, buffer+p, sz); p += sz; @@ -520,7 +520,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] rss.tgt = tgt; rss.p_block = 0; rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; - check_lseek(fd, tgt->pos[0] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET); if (style[0] == 'i') { // imgdiff ApplyImagePatch(buffer, src->size * BLOCKSIZE, @@ -620,7 +620,7 @@ Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) { int i, j; for (i = 0; i < rs->count; ++i) { - check_lseek(fd, rs->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET); for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) { readblock(fd, buffer, BLOCKSIZE); SHA_update(&ctx, buffer, BLOCKSIZE); -- cgit v1.2.3 From f7bb09dae8d7c89130648ef2aca7025860b6d801 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 4 Sep 2014 08:10:32 -0700 Subject: fix comment in blockimg updater code The comment for the DEBUG_ERASE setting is exactly backwards. Change-Id: I98ab5828365894217fc78976817a131e7d22d5c1 --- updater/blockimg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/updater/blockimg.c b/updater/blockimg.c index 6d412d5d5..555230dd3 100644 --- a/updater/blockimg.c +++ b/updater/blockimg.c @@ -37,9 +37,9 @@ #define BLOCKSIZE 4096 -// Set this to 1 to interpret 'erase' transfers to mean do a -// BLKDISCARD ioctl. Set to 0 to interpret erase to mean fill the -// region with zeroes. +// Set this to 0 to interpret 'erase' transfers to mean do a +// BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret +// erase to mean fill the region with zeroes. #define DEBUG_ERASE 0 #ifndef BLKDISCARD -- cgit v1.2.3 From 574443d8956802f35347cac7fae7eb16240e3c16 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 5 Sep 2014 08:22:12 -0700 Subject: create block map for all update packages on /data Always create the block map for packages on /data; don't only look at the encryptable/encrypted flags. Bug: 17395453 Change-Id: Iaa7643a32898328277841e324305b9419a9e071c --- uncrypt/uncrypt.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c index 07e5ae665..189fa57e1 100644 --- a/uncrypt/uncrypt.c +++ b/uncrypt/uncrypt.c @@ -160,8 +160,6 @@ char* parse_recovery_command_file() int count = 0; char temp[1024]; - - FILE* f = fopen(RECOVERY_COMMAND_FILE, "r"); if (f == NULL) { return NULL; @@ -405,16 +403,15 @@ int main(int argc, char** argv) ALOGI("encryptable: %s\n", encryptable ? "yes" : "no"); ALOGI(" encrypted: %s\n", encrypted ? "yes" : "no"); - if (!encryptable) { - // If the file is on a filesystem that doesn't support - // encryption (eg, /cache), then leave it alone. - // - // TODO: change this to be !encrypted -- if the file is on - // /data but /data isn't encrypted, we don't need to use the - // block map mechanism. We do for now so as to get more - // testing of it (since most dogfood devices aren't - // encrypted). - + // Recovery supports installing packages from 3 paths: /cache, + // /data, and /sdcard. (On a particular device, other locations + // may work, but those are three we actually expect.) + // + // On /data we want to convert the file to a block map so that we + // can read the package without mounting the partition. On /cache + // and /sdcard we leave the file alone. + if (strncmp(path, "/data/", 6) != 0) { + // path does not start with "/data/"; leave it alone. unlink(RECOVERY_COMMAND_FILE_TMP); } else { ALOGI("writing block map %s", map_file); -- cgit v1.2.3 From f9bc2a544d5189ef9279edb8b5d67913eec53855 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 5 Sep 2014 08:22:12 -0700 Subject: create block map for all update packages on /data Always create the block map for packages on /data; don't only look at the encryptable/encrypted flags. Bug: 17395453 Change-Id: Iaa7643a32898328277841e324305b9419a9e071c --- uncrypt/uncrypt.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c index 07e5ae665..189fa57e1 100644 --- a/uncrypt/uncrypt.c +++ b/uncrypt/uncrypt.c @@ -160,8 +160,6 @@ char* parse_recovery_command_file() int count = 0; char temp[1024]; - - FILE* f = fopen(RECOVERY_COMMAND_FILE, "r"); if (f == NULL) { return NULL; @@ -405,16 +403,15 @@ int main(int argc, char** argv) ALOGI("encryptable: %s\n", encryptable ? "yes" : "no"); ALOGI(" encrypted: %s\n", encrypted ? "yes" : "no"); - if (!encryptable) { - // If the file is on a filesystem that doesn't support - // encryption (eg, /cache), then leave it alone. - // - // TODO: change this to be !encrypted -- if the file is on - // /data but /data isn't encrypted, we don't need to use the - // block map mechanism. We do for now so as to get more - // testing of it (since most dogfood devices aren't - // encrypted). - + // Recovery supports installing packages from 3 paths: /cache, + // /data, and /sdcard. (On a particular device, other locations + // may work, but those are three we actually expect.) + // + // On /data we want to convert the file to a block map so that we + // can read the package without mounting the partition. On /cache + // and /sdcard we leave the file alone. + if (strncmp(path, "/data/", 6) != 0) { + // path does not start with "/data/"; leave it alone. unlink(RECOVERY_COMMAND_FILE_TMP); } else { ALOGI("writing block map %s", map_file); -- cgit v1.2.3 From a6e13ae01bff5109361868f565187f16d045c82d Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 24 Sep 2014 11:46:17 -0700 Subject: Include reason when wiping data. This will help us track down who requested a data wipe. Bug: 17412160 Change-Id: I1c439fbd29f96b9851810baca9101f683a0f18d8 --- recovery.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/recovery.cpp b/recovery.cpp index f78d6e46c..7f17b16ef 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -60,6 +60,7 @@ static const struct option OPTIONS[] = { { "locale", required_argument, NULL, 'l' }, { "stages", required_argument, NULL, 'g' }, { "shutdown_after", no_argument, NULL, 'p' }, + { "reason", required_argument, NULL, 'r' }, { NULL, 0, NULL, 0 }, }; @@ -80,6 +81,7 @@ RecoveryUI* ui = NULL; char* locale = NULL; char recovery_version[PROPERTY_VALUE_MAX+1]; char* stage = NULL; +char* reason = NULL; /* * The recovery tool communicates with the main system through /cache files. @@ -870,6 +872,7 @@ main(int argc, char **argv) { break; } case 'p': shutdown_after = true; break; + case 'r': reason = optarg; break; case '?': LOGE("Invalid command argument\n"); continue; @@ -881,6 +884,7 @@ main(int argc, char **argv) { } printf("locale is [%s]\n", locale); printf("stage is [%s]\n", stage); + printf("reason is [%s]\n", reason); Device* device = make_device(); ui = device->GetUI(); -- cgit v1.2.3 From d5b1727765060b2886d11cf7af9588b818be12cf Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Wed, 22 Oct 2014 14:28:23 -0700 Subject: Treat already-renamed files as having no problems. This should help with reentrant OTAs. Bug: 18079773 Change-Id: I102fd738e3b450483ecd4471384c12e89fc586e2 --- updater/install.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/updater/install.c b/updater/install.c index dad0d08c9..42dbb58da 100644 --- a/updater/install.c +++ b/updater/install.c @@ -358,6 +358,9 @@ Value* RenameFn(const char* name, State* state, int argc, Expr* argv[]) { if (make_parents(dst_name) != 0) { ErrorAbort(state, "Creating parent of %s failed, error %s", dst_name, strerror(errno)); + } else if (access(dst_name, F_OK) == 0 && access(src_name, F_OK) != 0) { + // File was already moved + result = dst_name; } else if (rename(src_name, dst_name) != 0) { ErrorAbort(state, "Rename of %s to %s failed, error %s", src_name, dst_name, strerror(errno)); -- cgit v1.2.3 From b8344b6f5908935ac2e36109c7433fde34646620 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Wed, 22 Oct 2014 18:38:48 -0700 Subject: Make /cache/recovery/last_log available in recovery Create a new recovery UI option to allow the user to view /cache/recovery/last_log for their device. This gives enhanced debugging information which may be necessary when a failed OTA occurs. Bug: 18094012 Change-Id: Ic3228de96e9bfc2a0141c7aab4ce392a38140cf3 --- default_device.cpp | 2 ++ device.h | 2 +- recovery.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 76 insertions(+), 5 deletions(-) diff --git a/default_device.cpp b/default_device.cpp index a25f05f8e..97806ac58 100644 --- a/default_device.cpp +++ b/default_device.cpp @@ -31,6 +31,7 @@ static const char* ITEMS[] = {"reboot system now", "wipe cache partition", "reboot to bootloader", "power down", + "view recovery logs", NULL }; class DefaultDevice : public Device { @@ -69,6 +70,7 @@ class DefaultDevice : public Device { case 3: return WIPE_CACHE; case 4: return REBOOT_BOOTLOADER; case 5: return SHUTDOWN; + case 6: return READ_RECOVERY_LASTLOG; default: return NO_ACTION; } } diff --git a/device.h b/device.h index 57ec3fc32..8ff4ec031 100644 --- a/device.h +++ b/device.h @@ -68,7 +68,7 @@ class Device { enum BuiltinAction { NO_ACTION, REBOOT, APPLY_EXT, APPLY_CACHE, // APPLY_CACHE is deprecated; has no effect APPLY_ADB_SIDELOAD, WIPE_DATA, WIPE_CACHE, - REBOOT_BOOTLOADER, SHUTDOWN }; + REBOOT_BOOTLOADER, SHUTDOWN, READ_RECOVERY_LASTLOG }; // Perform a recovery action selected from the menu. // 'menu_position' will be the item number of the selected menu diff --git a/recovery.cpp b/recovery.cpp index 7f17b16ef..e1a2a9678 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -77,6 +77,8 @@ static const char *SDCARD_ROOT = "/sdcard"; static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; +#define KEEP_LOG_COUNT 10 + RecoveryUI* ui = NULL; char* locale = NULL; char recovery_version[PROPERTY_VALUE_MAX+1]; @@ -161,6 +163,12 @@ fopen_path(const char *path, const char *mode) { return fp; } +static void redirect_stdio(const char* filename) { + // If these fail, there's not really anywhere to complain... + freopen(filename, "a", stdout); setbuf(stdout, NULL); + freopen(filename, "a", stderr); setbuf(stderr, NULL); +} + // close a file, log an error if the error indicator is set static void check_and_fclose(FILE *fp, const char *name) { @@ -665,6 +673,65 @@ wipe_data(int confirm, Device* device) { ui->Print("Data wipe complete.\n"); } +static void file_to_ui(const char* fn) { + FILE *fp = fopen_path(fn, "re"); + if (fp == NULL) { + ui->Print(" Unable to open %s: %s\n", fn, strerror(errno)); + return; + } + char line[1024]; + int ct = 0; + redirect_stdio("/dev/null"); + while(fgets(line, sizeof(line), fp) != NULL) { + ui->Print("%s", line); + ct++; + if (ct % 30 == 0) { + // give the user time to glance at the entries + ui->WaitKey(); + } + } + redirect_stdio(TEMPORARY_LOG_FILE); + fclose(fp); +} + +static void choose_recovery_file(Device* device) { + int i; + static const char** title_headers = NULL; + char *filename; + const char* headers[] = { "Select file to view", + "", + NULL }; + char* entries[KEEP_LOG_COUNT + 2]; + memset(entries, 0, sizeof(entries)); + + for (i = 0; i < KEEP_LOG_COUNT; i++) { + char *filename; + if (asprintf(&filename, (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), i) == -1) { + // memory allocation failure - return early. Should never happen. + return; + } + if ((ensure_path_mounted(filename) != 0) || (access(filename, R_OK) == -1)) { + free(filename); + entries[i+1] = NULL; + break; + } + entries[i+1] = filename; + } + + entries[0] = strdup("Go back"); + title_headers = prepend_title((const char**)headers); + + while(1) { + int chosen_item = get_menu_selection(title_headers, entries, 1, 0, device); + if (chosen_item == 0) break; + file_to_ui(entries[chosen_item]); + } + + for (i = 0; i < KEEP_LOG_COUNT + 1; i++) { + free(entries[i]); + } +} + // Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION // means to take the default, which is to reboot or shutdown depending // on if the --shutdown_after flag was passed to recovery. @@ -760,6 +827,10 @@ prompt_and_wait(Device* device, int status) { ui->Print("\nAPPLY_CACHE is deprecated.\n"); break; + case Device::READ_RECOVERY_LASTLOG: + choose_recovery_file(device); + break; + case Device::APPLY_ADB_SIDELOAD: status = apply_from_adb(ui, &wipe_cache, TEMPORARY_INSTALL_FILE); if (status >= 0) { @@ -824,9 +895,7 @@ int main(int argc, char **argv) { time_t start = time(NULL); - // If these fail, there's not really anywhere to complain... - freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL); - freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL); + redirect_stdio(TEMPORARY_LOG_FILE); // If this binary is started with the single argument "--adbd", // instead of being the normal recovery binary, it turns into kind @@ -844,7 +913,7 @@ main(int argc, char **argv) { load_volume_table(); ensure_path_mounted(LAST_LOG_FILE); - rotate_last_logs(10); + rotate_last_logs(KEEP_LOG_COUNT); get_args(&argc, &argv); const char *send_intent = NULL; -- cgit v1.2.3 From 7548025bdd111421cff2fe73839c01e2dc0a0365 Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Wed, 22 Oct 2014 19:48:41 -0700 Subject: Log to UI any metadata setting errors Bug: 18079773 Change-Id: Ic6fddbcbcb6ddb9e1cbd1698df98387c0033ae15 --- updater/install.c | 84 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 34 deletions(-) diff --git a/updater/install.c b/updater/install.c index 42dbb58da..17ea4c2b5 100644 --- a/updater/install.c +++ b/updater/install.c @@ -52,6 +52,26 @@ #include "wipe.h" #endif +void uiPrint(State* state, char* buffer) { + char* line = strtok(buffer, "\n"); + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); + while (line) { + fprintf(ui->cmd_pipe, "ui_print %s\n", line); + line = strtok(NULL, "\n"); + } + fprintf(ui->cmd_pipe, "ui_print\n"); +} + +__attribute__((__format__(printf, 2, 3))) __nonnull((2)) +void uiPrintf(State* state, const char* format, ...) { + char error_msg[1024]; + va_list ap; + va_start(ap, format); + vsnprintf(error_msg, sizeof(error_msg), format, ap); + va_end(ap); + uiPrint(state, error_msg); +} + // Take a sha-1 digest and return it as a newly-allocated hex string. char* PrintSha1(const uint8_t* digest) { char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1); @@ -633,7 +653,7 @@ struct perm_parsed_args { uint64_t capabilities; }; -static struct perm_parsed_args ParsePermArgs(int argc, char** args) { +static struct perm_parsed_args ParsePermArgs(State * state, int argc, char** args) { int i; struct perm_parsed_args parsed; int bad = 0; @@ -648,7 +668,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { parsed.uid = uid; parsed.has_uid = true; } else { - printf("ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]); bad++; } continue; @@ -659,7 +679,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { parsed.gid = gid; parsed.has_gid = true; } else { - printf("ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]); bad++; } continue; @@ -670,7 +690,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { parsed.mode = mode; parsed.has_mode = true; } else { - printf("ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]); bad++; } continue; @@ -681,7 +701,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { parsed.dmode = mode; parsed.has_dmode = true; } else { - printf("ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]); bad++; } continue; @@ -692,7 +712,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { parsed.fmode = mode; parsed.has_fmode = true; } else { - printf("ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]); bad++; } continue; @@ -703,7 +723,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { parsed.capabilities = capabilities; parsed.has_capabilities = true; } else { - printf("ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]); bad++; } continue; @@ -713,7 +733,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { parsed.selabel = args[i+1]; parsed.has_selabel = true; } else { - printf("ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]); bad++; } continue; @@ -730,6 +750,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { } static int ApplyParsedPerms( + State * state, const char* filename, const struct stat *statptr, struct perm_parsed_args parsed) @@ -743,39 +764,39 @@ static int ApplyParsedPerms( if (parsed.has_uid) { if (chown(filename, parsed.uid, -1) < 0) { - printf("ApplyParsedPerms: chown of %s to %d failed: %s\n", - filename, parsed.uid, strerror(errno)); + uiPrintf(state, "ApplyParsedPerms: chown of %s to %d failed: %s\n", + filename, parsed.uid, strerror(errno)); bad++; } } if (parsed.has_gid) { if (chown(filename, -1, parsed.gid) < 0) { - printf("ApplyParsedPerms: chgrp of %s to %d failed: %s\n", - filename, parsed.gid, strerror(errno)); + uiPrintf(state, "ApplyParsedPerms: chgrp of %s to %d failed: %s\n", + filename, parsed.gid, strerror(errno)); bad++; } } if (parsed.has_mode) { if (chmod(filename, parsed.mode) < 0) { - printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n", - filename, parsed.mode, strerror(errno)); + uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n", + filename, parsed.mode, strerror(errno)); bad++; } } if (parsed.has_dmode && S_ISDIR(statptr->st_mode)) { if (chmod(filename, parsed.dmode) < 0) { - printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n", - filename, parsed.dmode, strerror(errno)); + uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n", + filename, parsed.dmode, strerror(errno)); bad++; } } if (parsed.has_fmode && S_ISREG(statptr->st_mode)) { if (chmod(filename, parsed.fmode) < 0) { - printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n", + uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n", filename, parsed.fmode, strerror(errno)); bad++; } @@ -784,8 +805,8 @@ static int ApplyParsedPerms( if (parsed.has_selabel) { // TODO: Don't silently ignore ENOTSUP if (lsetfilecon(filename, parsed.selabel) && (errno != ENOTSUP)) { - printf("ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n", - filename, parsed.selabel, strerror(errno)); + uiPrintf(state, "ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n", + filename, parsed.selabel, strerror(errno)); bad++; } } @@ -794,7 +815,7 @@ static int ApplyParsedPerms( if (parsed.capabilities == 0) { if ((removexattr(filename, XATTR_NAME_CAPS) == -1) && (errno != ENODATA)) { // Report failure unless it's ENODATA (attribute not set) - printf("ApplyParsedPerms: removexattr of %s to %" PRIx64 " failed: %s\n", + uiPrintf(state, "ApplyParsedPerms: removexattr of %s to %" PRIx64 " failed: %s\n", filename, parsed.capabilities, strerror(errno)); bad++; } @@ -807,8 +828,8 @@ static int ApplyParsedPerms( cap_data.data[1].permitted = (uint32_t) (parsed.capabilities >> 32); cap_data.data[1].inheritable = 0; if (setxattr(filename, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) < 0) { - printf("ApplyParsedPerms: setcap of %s to %" PRIx64 " failed: %s\n", - filename, parsed.capabilities, strerror(errno)); + uiPrintf(state, "ApplyParsedPerms: setcap of %s to %" PRIx64 " failed: %s\n", + filename, parsed.capabilities, strerror(errno)); bad++; } } @@ -820,10 +841,11 @@ static int ApplyParsedPerms( // nftw doesn't allow us to pass along context, so we need to use // global variables. *sigh* static struct perm_parsed_args recursive_parsed_args; +static State* recursive_state; static int do_SetMetadataRecursive(const char* filename, const struct stat *statptr, int fileflags, struct FTW *pfwt) { - return ApplyParsedPerms(filename, statptr, recursive_parsed_args); + return ApplyParsedPerms(recursive_state, filename, statptr, recursive_parsed_args); } static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv[]) { @@ -848,14 +870,16 @@ static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv goto done; } - struct perm_parsed_args parsed = ParsePermArgs(argc, args); + struct perm_parsed_args parsed = ParsePermArgs(state, argc, args); if (recursive) { recursive_parsed_args = parsed; + recursive_state = state; bad += nftw(args[0], do_SetMetadataRecursive, 30, FTW_CHDIR | FTW_DEPTH | FTW_PHYS); memset(&recursive_parsed_args, 0, sizeof(recursive_parsed_args)); + recursive_state = NULL; } else { - bad += ApplyParsedPerms(args[0], &sb, parsed); + bad += ApplyParsedPerms(state, args[0], &sb, parsed); } done: @@ -1227,15 +1251,7 @@ Value* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) { } 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"); - + uiPrint(state, buffer); return StringValue(buffer); } -- cgit v1.2.3 From bd6138cffeedf1d60346ec9d34711fe38d0f8323 Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Wed, 22 Oct 2014 17:05:08 -0700 Subject: Allow passing of mount args to mountFn Bug: 18079773 Bug: 18092222 Change-Id: Ifc3f3e123de729dfbb2f49414b3207afa96268d5 --- updater/install.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/updater/install.c b/updater/install.c index 17ea4c2b5..282a6188b 100644 --- a/updater/install.c +++ b/updater/install.c @@ -91,16 +91,27 @@ char* PrintSha1(const uint8_t* digest) { // fs_type="ext4" partition_type="EMMC" location=device Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; - if (argc != 4) { - return ErrorAbort(state, "%s() expects 4 args, got %d", name, argc); + if (argc != 4 && argc != 5) { + return ErrorAbort(state, "%s() expects 4-5 args, got %d", name, argc); } char* fs_type; char* partition_type; char* location; char* mount_point; - if (ReadArgs(state, argv, 4, &fs_type, &partition_type, + char* mount_options; + bool has_mount_options; + if (argc == 5) { + has_mount_options = true; + if (ReadArgs(state, argv, 5, &fs_type, &partition_type, + &location, &mount_point, &mount_options) < 0) { + return NULL; + } + } else { + has_mount_options = false; + if (ReadArgs(state, argv, 4, &fs_type, &partition_type, &location, &mount_point) < 0) { - return NULL; + return NULL; + } } if (strlen(fs_type) == 0) { @@ -154,7 +165,8 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { result = mount_point; } else { if (mount(location, mount_point, fs_type, - MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) { + MS_NOATIME | MS_NODEV | MS_NODIRATIME, + has_mount_options ? mount_options : "") < 0) { printf("%s: failed to mount %s at %s: %s\n", name, location, mount_point, strerror(errno)); result = strdup(""); @@ -168,6 +180,7 @@ done: free(partition_type); free(location); if (result != mount_point) free(mount_point); + if (has_mount_options) free(mount_options); return StringValue(result); } -- cgit v1.2.3 From 6a821fe5f054df674151d54e7b03be63afd35d58 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Thu, 23 Oct 2014 20:36:42 -0700 Subject: unconditionally apply SELinux labels to symlinks At the end of the OTA script, we walk through /system, updating all the permissions on the filesystem, including the UID, GID, standard UNIX permissions, capabilities, and SELinux labels. In the case of a symbolic link, however, we want to skip most of those operations. The UID, GID, UNIX permissions, and capabilities don't meaningfully apply to symbolic links. However, that's not true with SELinux labels. The SELinux label on a symbolic link is important. We need to make sure the label on the symbolic link is always updated, even if none of the other attributes are updated. This change unconditionally updates the SELinux label on the symbolic link itself. lsetfilecon() is used, so that the link itself is updated, not what it's pointing to. In addition, drop the ENOTSUP special case. SELinux has been a requirement since Android 4.4. Running without filesystem extended attributes is no longer supported, and we shouldn't even try to handle non-SELinux updates anymore. (Note: this could be problematic if these scripts are ever used to produce OTA images for 4.2 devices) Bug: 18079773 Change-Id: I87f99a1c88fe02bb2914f1884cac23ce1b385f91 --- updater/install.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/updater/install.c b/updater/install.c index 282a6188b..db2bd3295 100644 --- a/updater/install.c +++ b/updater/install.c @@ -770,9 +770,17 @@ static int ApplyParsedPerms( { int bad = 0; + if (parsed.has_selabel) { + if (lsetfilecon(filename, parsed.selabel) != 0) { + uiPrintf(state, "ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n", + filename, parsed.selabel, strerror(errno)); + bad++; + } + } + /* ignore symlinks */ if (S_ISLNK(statptr->st_mode)) { - return 0; + return bad; } if (parsed.has_uid) { @@ -815,15 +823,6 @@ static int ApplyParsedPerms( } } - if (parsed.has_selabel) { - // TODO: Don't silently ignore ENOTSUP - if (lsetfilecon(filename, parsed.selabel) && (errno != ENOTSUP)) { - uiPrintf(state, "ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n", - filename, parsed.selabel, strerror(errno)); - bad++; - } - } - if (parsed.has_capabilities && S_ISREG(statptr->st_mode)) { if (parsed.capabilities == 0) { if ((removexattr(filename, XATTR_NAME_CAPS) == -1) && (errno != ENODATA)) { -- cgit v1.2.3 From f15e31edf948d9dac11858fba6dfdff8a5cd91c9 Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Fri, 24 Oct 2014 14:14:41 -0700 Subject: Log mount/unmount errors to UI Bug: 18092022 Change-Id: I6c42038ebeb1cfc1e7ca0d3e12310fdce1b990b0 --- updater/install.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/updater/install.c b/updater/install.c index db2bd3295..ff7de4793 100644 --- a/updater/install.c +++ b/updater/install.c @@ -151,13 +151,13 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { const MtdPartition* mtd; mtd = mtd_find_partition_by_name(location); if (mtd == NULL) { - printf("%s: no mtd partition named \"%s\"", + uiPrintf(state, "%s: no mtd partition named \"%s\"", name, location); result = strdup(""); goto done; } if (mtd_mount_partition(mtd, mount_point, fs_type, 0 /* rw */) != 0) { - printf("mtd mount of %s failed: %s\n", + uiPrintf(state, "mtd mount of %s failed: %s\n", location, strerror(errno)); result = strdup(""); goto done; @@ -167,7 +167,7 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { if (mount(location, mount_point, fs_type, MS_NOATIME | MS_NODEV | MS_NODIRATIME, has_mount_options ? mount_options : "") < 0) { - printf("%s: failed to mount %s at %s: %s\n", + uiPrintf(state, "%s: failed to mount %s at %s: %s\n", name, location, mount_point, strerror(errno)); result = strdup(""); } else { @@ -231,10 +231,14 @@ Value* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) { scan_mounted_volumes(); const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point); if (vol == NULL) { - printf("unmount of %s failed; no such volume\n", mount_point); + uiPrintf(state, "unmount of %s failed; no such volume\n", mount_point); result = strdup(""); } else { - unmount_mounted_volume(vol); + int ret = unmount_mounted_volume(vol); + if (ret != 0) { + uiPrintf(state, "unmount of %s failed (%d): %s\n", + mount_point, ret, strerror(errno)); + } result = mount_point; } -- cgit v1.2.3 From a6c142f2a579ea5e7cdfbc88e6a061c55029265a Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Tue, 28 Oct 2014 19:49:57 -0700 Subject: Force sync files written by minzip. Some files appear to be missing their sync to disk. Bug: 18145574 Change-Id: Ic858624a4dd65bbfc54d30f3a13c607078270345 --- minzip/Zip.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/minzip/Zip.c b/minzip/Zip.c index 5070104d3..70aff00cd 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -1067,7 +1067,8 @@ bool mzExtractRecursive(const ZipArchive *pArchive, setfscreatecon(secontext); } - int fd = creat(targetFile, UNZIP_FILEMODE); + int fd = open(targetFile, O_CREAT|O_WRONLY|O_TRUNC|O_SYNC + , UNZIP_FILEMODE); if (secontext) { freecon(secontext); @@ -1082,7 +1083,12 @@ bool mzExtractRecursive(const ZipArchive *pArchive, } bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd); - close(fd); + if (ok) { + ok = (fsync(fd) == 0); + } + if (close(fd) != 0) { + ok = false; + } if (!ok) { LOGE("Error extracting \"%s\"\n", targetFile); ok = false; -- cgit v1.2.3 From cddb68b5eafbeba696d5276bda1f1a9f70bbde42 Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Wed, 29 Oct 2014 12:42:15 -0700 Subject: Use more aggressive sync writing to applypatch. We have seen cases where the boot partition is patched, but upon recovery the partition appears to be corrupted. Open up all patched files/partitions with O_SYNC, and do not ignore the errors from fsync/close operations. Bug: 18170529 Change-Id: I392ad0a321d937c4ad02eaeea9170be384a4744b --- applypatch/applypatch.c | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index bfb9440e4..2c86e0984 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -309,7 +309,7 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { // Save the contents of the given FileContents object under the given // filename. Return 0 on success. int SaveFileContents(const char* filename, const FileContents* file) { - int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR); if (fd < 0) { printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno)); @@ -324,8 +324,14 @@ int SaveFileContents(const char* filename, const FileContents* file) { close(fd); return -1; } - fsync(fd); - close(fd); + if (fsync(fd) != 0) { + printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno)); + return -1; + } + if (close(fd) != 0) { + printf("close of \"%s\" failed: %s\n", filename, strerror(errno)); + return -1; + } if (chmod(filename, file->st.st_mode) != 0) { printf("chmod of \"%s\" failed: %s\n", filename, strerror(errno)); @@ -408,7 +414,7 @@ int WriteToPartition(unsigned char* data, size_t len, { size_t start = 0; int success = 0; - int fd = open(partition, O_RDWR); + int fd = open(partition, O_RDWR | O_SYNC); if (fd < 0) { printf("failed to open %s: %s\n", partition, strerror(errno)); return -1; @@ -433,7 +439,22 @@ int WriteToPartition(unsigned char* data, size_t len, } start += written; } - fsync(fd); + if (fsync(fd) != 0) { + printf("failed to sync to %s (%s)\n", + partition, strerror(errno)); + return -1; + } + if (close(fd) != 0) { + printf("failed to close %s (%s)\n", + partition, strerror(errno)); + return -1; + } + fd = open(partition, O_RDONLY); + if (fd < 0) { + printf("failed to reopen %s for verify (%s)\n", + partition, strerror(errno)); + return -1; + } // drop caches so our subsequent verification read // won't just be reading the cache. @@ -919,7 +940,8 @@ static int GenerateTarget(FileContents* source_file, strcpy(outname, target_filename); strcat(outname, ".patch"); - output = open(outname, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + output = open(outname, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, + S_IRUSR | S_IWUSR); if (output < 0) { printf("failed to open output file %s: %s\n", outname, strerror(errno)); @@ -950,8 +972,14 @@ static int GenerateTarget(FileContents* source_file, } if (output >= 0) { - fsync(output); - close(output); + if (fsync(output) != 0) { + printf("failed to fsync file \"%s\" (%s)\n", outname, strerror(errno)); + result = 1; + } + if (close(output) != 0) { + printf("failed to close file \"%s\" (%s)\n", outname, strerror(errno)); + result = 1; + } } if (result != 0) { -- cgit v1.2.3