diff options
author | Jean-Baptiste Queru <jbq@google.com> | 2009-07-26 02:48:00 +0200 |
---|---|---|
committer | Jean-Baptiste Queru <jbq@google.com> | 2009-07-26 02:48:00 +0200 |
commit | 7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7 (patch) | |
tree | 88c0fc6a69cade6f9d4437666c6420e0f757e278 | |
parent | Merge commit 'korg/cupcake' (diff) | |
parent | skip over all-zero blocks when reading MTD partition (diff) | |
download | android_bootable_recovery-7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7.tar android_bootable_recovery-7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7.tar.gz android_bootable_recovery-7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7.tar.bz2 android_bootable_recovery-7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7.tar.lz android_bootable_recovery-7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7.tar.xz android_bootable_recovery-7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7.tar.zst android_bootable_recovery-7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7.zip |
Diffstat (limited to '')
67 files changed, 2704 insertions, 1221 deletions
diff --git a/Android.mk b/Android.mk index 816d143cc..0367fef70 100644 --- a/Android.mk +++ b/Android.mk @@ -22,6 +22,9 @@ LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true +RECOVERY_API_VERSION := 2 +LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) + # This binary is in the recovery ramdisk, which is otherwise a copy of root. # It gets copied there in config/Makefile. LOCAL_MODULE_TAGS suppresses # a (redundant) copy of the binary in /system/bin for user builds. @@ -30,31 +33,20 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_TAGS := eng LOCAL_STATIC_LIBRARIES := libminzip libunz libamend libmtdutils libmincrypt -LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libcutils +LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils LOCAL_STATIC_LIBRARIES += libstdc++ libc -# Specify a C-includable file containing the OTA public keys. -# This is built in config/Makefile. -# *** THIS IS A TOTAL HACK; EXECUTABLES MUST NOT CHANGE BETWEEN DIFFERENT -# PRODUCTS/BUILD TYPES. *** -# TODO: make recovery read the keys from an external file. -RECOVERY_INSTALL_OTA_KEYS_INC := \ - $(call intermediates-dir-for,PACKAGING,ota_keys_inc)/keys.inc -# Let install.c say #include "keys.inc" -LOCAL_C_INCLUDES += $(dir $(RECOVERY_INSTALL_OTA_KEYS_INC)) - include $(BUILD_EXECUTABLE) -# Depend on the generated keys.inc containing the OTA public keys. -$(intermediates)/install.o: $(RECOVERY_INSTALL_OTA_KEYS_INC) - include $(commands_recovery_local_path)/minui/Android.mk - -endif # TARGET_ARCH == arm -endif # !TARGET_SIMULATOR - include $(commands_recovery_local_path)/amend/Android.mk include $(commands_recovery_local_path)/minzip/Android.mk include $(commands_recovery_local_path)/mtdutils/Android.mk include $(commands_recovery_local_path)/tools/Android.mk +include $(commands_recovery_local_path)/edify/Android.mk +include $(commands_recovery_local_path)/updater/Android.mk commands_recovery_local_path := + +endif # TARGET_ARCH == arm +endif # !TARGET_SIMULATOR + diff --git a/amend/Android.mk b/amend/Android.mk index ae2d44ae1..c3b7c3277 100644 --- a/amend/Android.mk +++ b/amend/Android.mk @@ -10,13 +10,11 @@ amend_src_files := \ ast.c \ symtab.c \ commands.c \ - permissions.c \ execute.c amend_test_files := \ test_symtab.c \ - test_commands.c \ - test_permissions.c + test_commands.c # "-x c" forces the lex/yacc files to be compiled as c; # the build system otherwise forces them to be c++. diff --git a/amend/amend.c b/amend/amend.c index 49cd64edb..6f706d021 100644 --- a/amend/amend.c +++ b/amend/amend.c @@ -17,6 +17,7 @@ #include <stdlib.h> #include "amend.h" #include "lexer.h" +#include "parser.h" extern const AmCommandList *gCommands; diff --git a/amend/commands.c b/amend/commands.c index 75ff82840..78121adf1 100644 --- a/amend/commands.c +++ b/amend/commands.c @@ -152,37 +152,30 @@ getCommandArgumentType(Command *cmd) } static int -callCommandInternal(CommandEntry *entry, int argc, const char *argv[], - PermissionRequestList *permissions) +callCommandInternal(CommandEntry *entry, int argc, const char *argv[]) { if (entry != NULL && entry->argType == CMD_ARGS_WORDS && (argc == 0 || (argc > 0 && argv != NULL))) { - if (permissions == NULL) { - int i; - for (i = 0; i < argc; i++) { - if (argv[i] == NULL) { - goto bail; - } + int i; + for (i = 0; i < argc; i++) { + if (argv[i] == NULL) { + goto bail; } } TRACE("calling command %s\n", entry->name); - return entry->hook(entry->name, entry->cookie, argc, argv, permissions); -//xxx if permissions, make sure the entry has added at least one element. + return entry->hook(entry->name, entry->cookie, argc, argv); } bail: return -1; } static int -callBooleanCommandInternal(CommandEntry *entry, bool arg, - PermissionRequestList *permissions) +callBooleanCommandInternal(CommandEntry *entry, bool arg) { if (entry != NULL && entry->argType == CMD_ARGS_BOOLEAN) { TRACE("calling boolean command %s\n", entry->name); - return entry->hook(entry->name, entry->cookie, arg ? 1 : 0, NULL, - permissions); -//xxx if permissions, make sure the entry has added at least one element. + return entry->hook(entry->name, entry->cookie, arg ? 1 : 0, NULL); } return -1; } @@ -190,63 +183,37 @@ callBooleanCommandInternal(CommandEntry *entry, bool arg, int callCommand(Command *cmd, int argc, const char *argv[]) { - return callCommandInternal((CommandEntry *)cmd, argc, argv, NULL); + return callCommandInternal((CommandEntry *)cmd, argc, argv); } int callBooleanCommand(Command *cmd, bool arg) { - return callBooleanCommandInternal((CommandEntry *)cmd, arg, NULL); -} - -int -getCommandPermissions(Command *cmd, int argc, const char *argv[], - PermissionRequestList *permissions) -{ - if (permissions != NULL) { - return callCommandInternal((CommandEntry *)cmd, argc, argv, - permissions); - } - return -1; -} - -int -getBooleanCommandPermissions(Command *cmd, bool arg, - PermissionRequestList *permissions) -{ - if (permissions != NULL) { - return callBooleanCommandInternal((CommandEntry *)cmd, arg, - permissions); - } - return -1; + return callBooleanCommandInternal((CommandEntry *)cmd, arg); } int callFunctionInternal(CommandEntry *entry, int argc, const char *argv[], - char **result, size_t *resultLen, PermissionRequestList *permissions) + char **result, size_t *resultLen) { if (entry != NULL && entry->argType == CMD_ARGS_WORDS && (argc == 0 || (argc > 0 && argv != NULL))) { - if ((permissions == NULL && result != NULL) || - (permissions != NULL && result == NULL)) + if (result != NULL) { - if (permissions == NULL) { - /* This is the actual invocation of the function, - * which means that none of the arguments are allowed - * to be NULL. - */ - int i; - for (i = 0; i < argc; i++) { - if (argv[i] == NULL) { - goto bail; - } + /* This is the actual invocation of the function, + * which means that none of the arguments are allowed + * to be NULL. + */ + int i; + for (i = 0; i < argc; i++) { + if (argv[i] == NULL) { + goto bail; } } TRACE("calling function %s\n", entry->name); return ((FunctionHook)entry->hook)(entry->name, entry->cookie, - argc, argv, result, resultLen, permissions); -//xxx if permissions, make sure the entry has added at least one element. + argc, argv, result, resultLen); } } bail: @@ -258,16 +225,5 @@ callFunction(Function *fn, int argc, const char *argv[], char **result, size_t *resultLen) { return callFunctionInternal((CommandEntry *)fn, argc, argv, - result, resultLen, NULL); -} - -int -getFunctionPermissions(Function *fn, int argc, const char *argv[], - PermissionRequestList *permissions) -{ - if (permissions != NULL) { - return callFunctionInternal((CommandEntry *)fn, argc, argv, - NULL, NULL, permissions); - } - return -1; + result, resultLen); } diff --git a/amend/commands.h b/amend/commands.h index 38931c075..6c97e5587 100644 --- a/amend/commands.h +++ b/amend/commands.h @@ -14,31 +14,18 @@ * limitations under the License. */ +#include <stdbool.h> + #ifndef AMEND_COMMANDS_H_ #define AMEND_COMMANDS_H_ -#include "permissions.h" - -/* Invoke or dry-run a command. If "permissions" is non-NULL, - * the hook should fill it out with the list of files and operations that - * it would need to complete its operation. If "permissions" is NULL, - * the hook should do the actual work specified by its arguments. - * - * When a command is called with non-NULL "permissions", some arguments - * may be NULL. A NULL argument indicates that the argument is actually - * the output of another function, so is not known at permissions time. - * The permissions of leaf-node functions (those that have only literal - * strings as arguments) will get appended to the permissions of the - * functions that call them. However, to be completely safe, functions - * that receive a NULL argument should request the broadest-possible - * permissions for the range of the input argument. +/* Invoke a command. * * When a boolean command is called, "argc" is the boolean value and * "argv" is NULL. */ typedef int (*CommandHook)(const char *name, void *cookie, - int argc, const char *argv[], - PermissionRequestList *permissions); + int argc, const char *argv[]); int commandInit(void); void commandCleanup(void); @@ -66,19 +53,13 @@ CommandArgumentType getCommandArgumentType(Command *cmd); int callCommand(Command *cmd, int argc, const char *argv[]); int callBooleanCommand(Command *cmd, bool arg); -int getCommandPermissions(Command *cmd, int argc, const char *argv[], - PermissionRequestList *permissions); -int getBooleanCommandPermissions(Command *cmd, bool arg, - PermissionRequestList *permissions); - /* * Function management */ typedef int (*FunctionHook)(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions); + char **result, size_t *resultLen); struct Function; typedef struct Function Function; @@ -90,7 +71,4 @@ Function *findFunction(const char *name); int callFunction(Function *fn, int argc, const char *argv[], char **result, size_t *resultLen); -int getFunctionPermissions(Function *fn, int argc, const char *argv[], - PermissionRequestList *permissions); - #endif // AMEND_COMMANDS_H_ diff --git a/amend/main.c b/amend/main.c index 9bb0785de..bc9e58778 100644 --- a/amend/main.c +++ b/amend/main.c @@ -86,12 +86,6 @@ main(int argc, char *argv[]) fprintf(stderr, "test_cmd_fn() failed: %d\n", ret); exit(ret); } - extern int test_permissions(void); - ret = test_permissions(); - if (ret != 0) { - fprintf(stderr, "test_permissions() failed: %d\n", ret); - exit(ret); - } #endif argc--; diff --git a/amend/permissions.c b/amend/permissions.c deleted file mode 100644 index a642d0bb2..000000000 --- a/amend/permissions.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdlib.h> -#include <string.h> -#include "permissions.h" - -int -initPermissionRequestList(PermissionRequestList *list) -{ - if (list != NULL) { - list->requests = NULL; - list->numRequests = 0; - list->requestsAllocated = 0; - return 0; - } - return -1; -} - -int -addPermissionRequestToList(PermissionRequestList *list, - const char *path, bool recursive, unsigned int permissions) -{ - if (list == NULL || list->numRequests < 0 || - list->requestsAllocated < list->numRequests || path == NULL) - { - return -1; - } - - if (list->numRequests == list->requestsAllocated) { - int newSize; - PermissionRequest *newRequests; - - newSize = list->requestsAllocated * 2; - if (newSize < 16) { - newSize = 16; - } - newRequests = (PermissionRequest *)realloc(list->requests, - newSize * sizeof(PermissionRequest)); - if (newRequests == NULL) { - return -2; - } - list->requests = newRequests; - list->requestsAllocated = newSize; - } - - PermissionRequest *req; - req = &list->requests[list->numRequests++]; - req->path = strdup(path); - if (req->path == NULL) { - list->numRequests--; - return -3; - } - req->recursive = recursive; - req->requested = permissions; - req->allowed = 0; - - return 0; -} - -void -freePermissionRequestListElements(PermissionRequestList *list) -{ - if (list != NULL && list->numRequests >= 0 && - list->requestsAllocated >= list->numRequests) - { - int i; - for (i = 0; i < list->numRequests; i++) { - free((void *)list->requests[i].path); - } - free(list->requests); - initPermissionRequestList(list); - } -} - -/* - * Global permission table - */ - -static struct { - Permission *permissions; - int numPermissionEntries; - int allocatedPermissionEntries; - bool permissionStateInitialized; -} gPermissionState = { -#if 1 - NULL, 0, 0, false -#else - .permissions = NULL, - .numPermissionEntries = 0, - .allocatedPermissionEntries = 0, - .permissionStateInitialized = false -#endif -}; - -int -permissionInit() -{ - if (gPermissionState.permissionStateInitialized) { - return -1; - } - gPermissionState.permissions = NULL; - gPermissionState.numPermissionEntries = 0; - gPermissionState.allocatedPermissionEntries = 0; - gPermissionState.permissionStateInitialized = true; -//xxx maybe add an "namespace root gets no permissions" fallback by default - return 0; -} - -void -permissionCleanup() -{ - if (gPermissionState.permissionStateInitialized) { - gPermissionState.permissionStateInitialized = false; - if (gPermissionState.permissions != NULL) { - int i; - for (i = 0; i < gPermissionState.numPermissionEntries; i++) { - free((void *)gPermissionState.permissions[i].path); - } - free(gPermissionState.permissions); - } - } -} - -int -getPermissionCount() -{ - if (gPermissionState.permissionStateInitialized) { - return gPermissionState.numPermissionEntries; - } - return -1; -} - -const Permission * -getPermissionAt(int index) -{ - if (!gPermissionState.permissionStateInitialized) { - return NULL; - } - if (index < 0 || index >= gPermissionState.numPermissionEntries) { - return NULL; - } - return &gPermissionState.permissions[index]; -} - -int -getAllowedPermissions(const char *path, bool recursive, - unsigned int *outAllowed) -{ - if (!gPermissionState.permissionStateInitialized) { - return -2; - } - if (outAllowed == NULL) { - return -1; - } - *outAllowed = 0; - if (path == NULL) { - return -1; - } - //TODO: implement this for real. - recursive = false; - *outAllowed = PERMSET_ALL; - return 0; -} - -int -countPermissionConflicts(PermissionRequestList *requests, bool updateAllowed) -{ - if (!gPermissionState.permissionStateInitialized) { - return -2; - } - if (requests == NULL || requests->requests == NULL || - requests->numRequests < 0 || - requests->requestsAllocated < requests->numRequests) - { - return -1; - } - int conflicts = 0; - int i; - for (i = 0; i < requests->numRequests; i++) { - PermissionRequest *req; - unsigned int allowed; - int ret; - - req = &requests->requests[i]; - ret = getAllowedPermissions(req->path, req->recursive, &allowed); - if (ret < 0) { - return ret; - } - if ((req->requested & ~allowed) != 0) { - conflicts++; - } - if (updateAllowed) { - req->allowed = allowed; - } - } - return conflicts; -} - -int -registerPermissionSet(int count, Permission *set) -{ - if (!gPermissionState.permissionStateInitialized) { - return -2; - } - if (count < 0 || (count > 0 && set == NULL)) { - return -1; - } - if (count == 0) { - return 0; - } - - if (gPermissionState.numPermissionEntries + count >= - gPermissionState.allocatedPermissionEntries) - { - Permission *newList; - int newSize; - - newSize = (gPermissionState.allocatedPermissionEntries + count) * 2; - if (newSize < 16) { - newSize = 16; - } - newList = (Permission *)realloc(gPermissionState.permissions, - newSize * sizeof(Permission)); - if (newList == NULL) { - return -3; - } - gPermissionState.permissions = newList; - gPermissionState.allocatedPermissionEntries = newSize; - } - - Permission *p = &gPermissionState.permissions[ - gPermissionState.numPermissionEntries]; - int i; - for (i = 0; i < count; i++) { - *p = set[i]; - //TODO: cache the strlen of the path - //TODO: normalize; strip off trailing / - p->path = strdup(p->path); - if (p->path == NULL) { - /* If we can't add all of the entries, we don't - * add any of them. - */ - Permission *pp = &gPermissionState.permissions[ - gPermissionState.numPermissionEntries]; - while (pp != p) { - free((void *)pp->path); - pp++; - } - return -4; - } - p++; - } - gPermissionState.numPermissionEntries += count; - - return 0; -} diff --git a/amend/permissions.h b/amend/permissions.h deleted file mode 100644 index 5b1d14dc2..000000000 --- a/amend/permissions.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AMEND_PERMISSIONS_H_ -#define AMEND_PERMISSIONS_H_ - -#include <stdbool.h> - -#define PERM_NONE (0) -#define PERM_STAT (1<<0) -#define PERM_READ (1<<1) -#define PERM_WRITE (1<<2) // including create, delete, mkdir, rmdir -#define PERM_CHMOD (1<<3) -#define PERM_CHOWN (1<<4) -#define PERM_CHGRP (1<<5) -#define PERM_SETUID (1<<6) -#define PERM_SETGID (1<<7) - -#define PERMSET_READ (PERM_STAT | PERM_READ) -#define PERMSET_WRITE (PERMSET_READ | PERM_WRITE) - -#define PERMSET_ALL \ - (PERM_STAT | PERM_READ | PERM_WRITE | PERM_CHMOD | \ - PERM_CHOWN | PERM_CHGRP | PERM_SETUID | PERM_SETGID) - -typedef struct { - unsigned int requested; - unsigned int allowed; - const char *path; - bool recursive; -} PermissionRequest; - -typedef struct { - PermissionRequest *requests; - int numRequests; - int requestsAllocated; -} PermissionRequestList; - -/* Properly clear out a PermissionRequestList. - * - * @return 0 if list is non-NULL, negative otherwise. - */ -int initPermissionRequestList(PermissionRequestList *list); - -/* Add a permission request to the list, allocating more space - * if necessary. - * - * @return 0 on success or a negative value on failure. - */ -int addPermissionRequestToList(PermissionRequestList *list, - const char *path, bool recursive, unsigned int permissions); - -/* Free anything allocated by addPermissionRequestToList(). The caller - * is responsible for freeing the actual PermissionRequestList. - */ -void freePermissionRequestListElements(PermissionRequestList *list); - - -/* - * Global permission table - */ - -typedef struct { - const char *path; - unsigned int allowed; -} Permission; - -int permissionInit(void); -void permissionCleanup(void); - -/* Returns the allowed permissions for the path in "outAllowed". - * Returns 0 if successful, negative if a parameter or global state - * is bad. - */ -int getAllowedPermissions(const char *path, bool recursive, - unsigned int *outAllowed); - -/* More-recently-registered permissions override older permissions. - */ -int registerPermissionSet(int count, Permission *set); - -/* Check to make sure that each request is allowed. - * - * @param requests The list of permission requests - * @param updateAllowed If true, update the "allowed" field in each - * element of the list - * @return the number of requests that were denied, or negative if - * an error occurred. - */ -int countPermissionConflicts(PermissionRequestList *requests, - bool updateAllowed); - -/* Inspection/testing/debugging functions - */ -int getPermissionCount(void); -const Permission *getPermissionAt(int index); - -#endif // AMEND_PERMISSIONS_H_ diff --git a/amend/register.c b/amend/register.c index 167dd32e5..0f44b7478 100644 --- a/amend/register.c +++ b/amend/register.c @@ -39,40 +39,15 @@ if (argc < 0) return -1; \ assert(argc == 0 || argv != NULL); \ if (argc != 0 && argv == NULL) return -1; \ - if (permissions != NULL) { \ - int CW_I_; \ - for (CW_I_ = 0; CW_I_ < argc; CW_I_++) { \ - assert(argv[CW_I_] != NULL); \ - if (argv[CW_I_] == NULL) return -1; \ - } \ - } \ } while (false) #define CHECK_FN() \ do { \ CHECK_WORDS(); \ - if (permissions != NULL) { \ - assert(result == NULL); \ - if (result != NULL) return -1; \ - } else { \ - assert(result != NULL); \ - if (result == NULL) return -1; \ - } \ + assert(result != NULL); \ + if (result == NULL) return -1; \ } while (false) -#define NO_PERMS(perms) \ - do { \ - PermissionRequestList *NP_PRL_ = (perms); \ - if (NP_PRL_ != NULL) { \ - int NP_RET_ = addPermissionRequestToList(NP_PRL_, \ - "", false, PERM_NONE); \ - if (NP_RET_ < 0) { \ - /* Returns from the calling function. \ - */ \ - return NP_RET_; \ - } \ - } \ - } while (false) /* * Command definitions @@ -81,13 +56,11 @@ /* assert <boolexpr> */ static int -cmd_assert(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_assert(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); CHECK_BOOL(); - NO_PERMS(permissions); /* If our argument is false, return non-zero (failure) * If our argument is true, return zero (success) @@ -102,8 +75,7 @@ cmd_assert(const char *name, void *cookie, int argc, const char *argv[], /* format <root> */ static int -cmd_format(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_format(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); @@ -115,8 +87,7 @@ cmd_format(const char *name, void *cookie, int argc, const char *argv[], /* copy_dir <srcdir> <dstdir> */ static int -cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); @@ -128,8 +99,7 @@ cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[], /* mark <resource> dirty|clean */ static int -cmd_mark(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_mark(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); @@ -144,8 +114,7 @@ cmd_mark(const char *name, void *cookie, int argc, const char *argv[], /* done */ static int -cmd_done(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_done(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); @@ -174,11 +143,6 @@ registerUpdateCommands() ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, NULL); if (ret < 0) return ret; -//xxx some way to fix permissions -//xxx could have "installperms" commands that build the fs_config list -//xxx along with a "commitperms", and any copy_dir etc. needs to see -// a commitperms before it will work - return 0; } @@ -194,13 +158,11 @@ registerUpdateCommands() */ static int fn_update_forced(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc != 0) { fprintf(stderr, "%s: wrong number of arguments (%d)\n", @@ -228,13 +190,11 @@ fn_update_forced(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_get_mark(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc != 1) { fprintf(stderr, "%s: wrong number of arguments (%d)\n", @@ -255,8 +215,7 @@ fn_get_mark(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { int ret = -1; @@ -273,23 +232,12 @@ fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[], dir = argv[0]; } - if (permissions != NULL) { - if (dir == NULL) { - /* The argument is the result of another function. - * Assume the worst case, where the function returns - * the root. - */ - dir = "/"; - } - ret = addPermissionRequestToList(permissions, dir, true, PERM_READ); - } else { //xxx build and return the string - *result = strdup("hashvalue"); - if (resultLen != NULL) { - *resultLen = strlen(*result); - } - ret = 0; + *result = strdup("hashvalue"); + if (resultLen != NULL) { + *resultLen = strlen(*result); } + ret = 0; return ret; } @@ -302,13 +250,11 @@ fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_matches(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc < 2) { fprintf(stderr, "%s: not enough arguments (%d < 2)\n", @@ -339,13 +285,11 @@ fn_matches(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_concat(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); size_t totalLen = 0; int i; diff --git a/amend/test_commands.c b/amend/test_commands.c index be938ac26..452f808b0 100644 --- a/amend/test_commands.c +++ b/amend/test_commands.c @@ -27,34 +27,30 @@ static struct { void *cookie; int argc; const char **argv; - PermissionRequestList *permissions; int returnValue; char *functionResult; } gTestCommandState; static int -testCommand(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +testCommand(const char *name, void *cookie, int argc, const char *argv[]) { gTestCommandState.called = true; gTestCommandState.name = name; gTestCommandState.cookie = cookie; gTestCommandState.argc = argc; gTestCommandState.argv = argv; - gTestCommandState.permissions = permissions; return gTestCommandState.returnValue; } static int testFunction(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, PermissionRequestList *permissions) + char **result, size_t *resultLen) { gTestCommandState.called = true; gTestCommandState.name = name; gTestCommandState.cookie = cookie; gTestCommandState.argc = argc; gTestCommandState.argv = argv; - gTestCommandState.permissions = permissions; if (result != NULL) { *result = gTestCommandState.functionResult; if (resultLen != NULL) { @@ -187,7 +183,6 @@ test_commands() memset(&gTestCommandState, 0, sizeof(gTestCommandState)); gTestCommandState.called = false; gTestCommandState.returnValue = 25; - gTestCommandState.permissions = (PermissionRequestList *)1; ret = callCommand(cmd, argc, argv); //xxx also try calling with a null argv element (should fail) assert(ret == 25); @@ -196,7 +191,6 @@ test_commands() assert(gTestCommandState.cookie == &gTestCommandState); assert(gTestCommandState.argc == argc); assert(gTestCommandState.argv == argv); - assert(gTestCommandState.permissions == NULL); /* Make a boolean call and make sure that it occurred. */ @@ -206,7 +200,6 @@ test_commands() memset(&gTestCommandState, 0, sizeof(gTestCommandState)); gTestCommandState.called = false; gTestCommandState.returnValue = 12; - gTestCommandState.permissions = (PermissionRequestList *)1; ret = callBooleanCommand(cmd, false); assert(ret == 12); assert(gTestCommandState.called); @@ -214,12 +207,10 @@ test_commands() assert(gTestCommandState.cookie == &gTestCommandState); assert(gTestCommandState.argc == 0); assert(gTestCommandState.argv == NULL); - assert(gTestCommandState.permissions == NULL); memset(&gTestCommandState, 0, sizeof(gTestCommandState)); gTestCommandState.called = false; gTestCommandState.returnValue = 13; - gTestCommandState.permissions = (PermissionRequestList *)1; ret = callBooleanCommand(cmd, true); assert(ret == 13); assert(gTestCommandState.called); @@ -227,45 +218,6 @@ test_commands() assert(gTestCommandState.cookie == &gTestCommandState); assert(gTestCommandState.argc == 1); assert(gTestCommandState.argv == NULL); - assert(gTestCommandState.permissions == NULL); - - /* Try looking up permissions. - */ - PermissionRequestList permissions; - cmd = findCommand("one"); - assert(cmd != NULL); - memset(&gTestCommandState, 0, sizeof(gTestCommandState)); - gTestCommandState.called = false; - gTestCommandState.returnValue = 27; - gTestCommandState.permissions = (PermissionRequestList *)1; - argv[1] = NULL; // null out an arg, which should be ok - ret = getCommandPermissions(cmd, argc, argv, &permissions); - assert(ret == 27); - assert(gTestCommandState.called); - assert(strcmp(gTestCommandState.name, "one") == 0); - assert(gTestCommandState.cookie == &gTestCommandState); - assert(gTestCommandState.argc == argc); - assert(gTestCommandState.argv == argv); - assert(gTestCommandState.permissions == &permissions); - - /* Boolean command permissions - */ - cmd = findCommand("bool"); - assert(cmd != NULL); - memset(&gTestCommandState, 0, sizeof(gTestCommandState)); - gTestCommandState.called = false; - gTestCommandState.returnValue = 55; - gTestCommandState.permissions = (PermissionRequestList *)1; - // argv[1] is still NULL - ret = getBooleanCommandPermissions(cmd, true, &permissions); - assert(ret == 55); - assert(gTestCommandState.called); - assert(strcmp(gTestCommandState.name, "bool") == 0); - assert(gTestCommandState.cookie == &gTestCommandState); - assert(gTestCommandState.argc == 1); - assert(gTestCommandState.argv == NULL); - assert(gTestCommandState.permissions == &permissions); - /* Smoke test commandCleanup(). */ @@ -365,7 +317,6 @@ test_functions() gTestCommandState.called = false; gTestCommandState.returnValue = 25; gTestCommandState.functionResult = "1234"; - gTestCommandState.permissions = (PermissionRequestList *)1; functionResult = NULL; functionResultLen = 55; ret = callFunction(fn, argc, argv, @@ -378,29 +329,9 @@ test_functions() assert(gTestCommandState.cookie == &gTestCommandState); assert(gTestCommandState.argc == argc); assert(gTestCommandState.argv == argv); - assert(gTestCommandState.permissions == NULL); assert(strcmp(functionResult, "1234") == 0); assert(functionResultLen == strlen(functionResult)); - /* Try looking up permissions. - */ - PermissionRequestList permissions; - fn = findFunction("one"); - assert(fn != NULL); - memset(&gTestCommandState, 0, sizeof(gTestCommandState)); - gTestCommandState.called = false; - gTestCommandState.returnValue = 27; - gTestCommandState.permissions = (PermissionRequestList *)1; - argv[1] = NULL; // null out an arg, which should be ok - ret = getFunctionPermissions(fn, argc, argv, &permissions); - assert(ret == 27); - assert(gTestCommandState.called); - assert(strcmp(gTestCommandState.name, "one") == 0); - assert(gTestCommandState.cookie == &gTestCommandState); - assert(gTestCommandState.argc == argc); - assert(gTestCommandState.argv == argv); - assert(gTestCommandState.permissions == &permissions); - /* Smoke test commandCleanup(). */ commandCleanup(); @@ -470,7 +401,6 @@ test_interaction() memset(&gTestCommandState, 0, sizeof(gTestCommandState)); gTestCommandState.called = false; gTestCommandState.returnValue = 123; - gTestCommandState.permissions = (PermissionRequestList *)1; ret = callCommand(cmd, argc, argv); assert(ret == 123); assert(gTestCommandState.called); @@ -478,7 +408,6 @@ test_interaction() assert((int)gTestCommandState.cookie == 0xc1); assert(gTestCommandState.argc == argc); assert(gTestCommandState.argv == argv); - assert(gTestCommandState.permissions == NULL); /* Call the overlapping function and make sure that the cookie is correct. */ @@ -490,7 +419,6 @@ test_interaction() gTestCommandState.called = false; gTestCommandState.returnValue = 125; gTestCommandState.functionResult = "5678"; - gTestCommandState.permissions = (PermissionRequestList *)2; functionResult = NULL; functionResultLen = 66; ret = callFunction(fn, argc, argv, &functionResult, &functionResultLen); @@ -500,7 +428,6 @@ test_interaction() assert((int)gTestCommandState.cookie == 0xf1); assert(gTestCommandState.argc == argc); assert(gTestCommandState.argv == argv); - assert(gTestCommandState.permissions == NULL); assert(strcmp(functionResult, "5678") == 0); assert(functionResultLen == strlen(functionResult)); diff --git a/amend/test_permissions.c b/amend/test_permissions.c deleted file mode 100644 index c3894563e..000000000 --- a/amend/test_permissions.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#undef NDEBUG -#include <assert.h> -#include "permissions.h" - -static int -test_permission_list() -{ - PermissionRequestList list; - int ret; - int numRequests; - - /* Bad parameter - */ - ret = initPermissionRequestList(NULL); - assert(ret < 0); - - /* Good parameter - */ - ret = initPermissionRequestList(&list); - assert(ret == 0); - - /* Bad parameters - */ - ret = addPermissionRequestToList(NULL, NULL, false, 0); - assert(ret < 0); - - ret = addPermissionRequestToList(&list, NULL, false, 0); - assert(ret < 0); - - /* Good parameters - */ - numRequests = 0; - - ret = addPermissionRequestToList(&list, "one", false, 1); - assert(ret == 0); - numRequests++; - - ret = addPermissionRequestToList(&list, "two", false, 2); - assert(ret == 0); - numRequests++; - - ret = addPermissionRequestToList(&list, "three", false, 3); - assert(ret == 0); - numRequests++; - - ret = addPermissionRequestToList(&list, "recursive", true, 55); - assert(ret == 0); - numRequests++; - - /* Validate the list - */ - assert(list.requests != NULL); - assert(list.numRequests == numRequests); - assert(list.numRequests <= list.requestsAllocated); - bool sawOne = false; - bool sawTwo = false; - bool sawThree = false; - bool sawRecursive = false; - int i; - for (i = 0; i < list.numRequests; i++) { - PermissionRequest *req = &list.requests[i]; - assert(req->allowed == 0); - - /* Order isn't guaranteed, so we have to switch every time. - */ - if (strcmp(req->path, "one") == 0) { - assert(!sawOne); - assert(req->requested == 1); - assert(!req->recursive); - sawOne = true; - } else if (strcmp(req->path, "two") == 0) { - assert(!sawTwo); - assert(req->requested == 2); - assert(!req->recursive); - sawTwo = true; - } else if (strcmp(req->path, "three") == 0) { - assert(!sawThree); - assert(req->requested == 3); - assert(!req->recursive); - sawThree = true; - } else if (strcmp(req->path, "recursive") == 0) { - assert(!sawRecursive); - assert(req->requested == 55); - assert(req->recursive); - sawRecursive = true; - } else { - assert(false); - } - } - assert(sawOne); - assert(sawTwo); - assert(sawThree); - assert(sawRecursive); - - /* Smoke test the teardown - */ - freePermissionRequestListElements(&list); - - return 0; -} - -static int -test_permission_table() -{ - int ret; - - /* Test the global permissions table. - * Try calling functions without initializing first. - */ - ret = registerPermissionSet(0, NULL); - assert(ret < 0); - - ret = countPermissionConflicts((PermissionRequestList *)16, false); - assert(ret < 0); - - ret = getPermissionCount(); - assert(ret < 0); - - const Permission *p; - p = getPermissionAt(0); - assert(p == NULL); - - /* Initialize. - */ - ret = permissionInit(); - assert(ret == 0); - - /* Make sure we can't initialize twice. - */ - ret = permissionInit(); - assert(ret < 0); - - /* Test the inspection functions. - */ - ret = getPermissionCount(); - assert(ret == 0); - - p = getPermissionAt(-1); - assert(p == NULL); - - p = getPermissionAt(0); - assert(p == NULL); - - p = getPermissionAt(1); - assert(p == NULL); - - /* Test registerPermissionSet(). - * Try some bad parameter values. - */ - ret = registerPermissionSet(-1, NULL); - assert(ret < 0); - - ret = registerPermissionSet(1, NULL); - assert(ret < 0); - - /* Register some permissions. - */ - Permission p1; - p1.path = "one"; - p1.allowed = 1; - ret = registerPermissionSet(1, &p1); - assert(ret == 0); - ret = getPermissionCount(); - assert(ret == 1); - - Permission p2[2]; - p2[0].path = "two"; - p2[0].allowed = 2; - p2[1].path = "three"; - p2[1].allowed = 3; - ret = registerPermissionSet(2, p2); - assert(ret == 0); - ret = getPermissionCount(); - assert(ret == 3); - - ret = registerPermissionSet(0, NULL); - assert(ret == 0); - ret = getPermissionCount(); - assert(ret == 3); - - p1.path = "four"; - p1.allowed = 4; - ret = registerPermissionSet(1, &p1); - assert(ret == 0); - - /* Make sure the table looks correct. - * Order is important; more-recent additions - * should appear at higher indices. - */ - ret = getPermissionCount(); - assert(ret == 4); - - int i; - for (i = 0; i < ret; i++) { - const Permission *p; - p = getPermissionAt(i); - assert(p != NULL); - assert(p->allowed == (unsigned int)(i + 1)); - switch (i) { - case 0: - assert(strcmp(p->path, "one") == 0); - break; - case 1: - assert(strcmp(p->path, "two") == 0); - break; - case 2: - assert(strcmp(p->path, "three") == 0); - break; - case 3: - assert(strcmp(p->path, "four") == 0); - break; - default: - assert(!"internal error"); - break; - } - } - p = getPermissionAt(ret); - assert(p == NULL); - - /* Smoke test the teardown - */ - permissionCleanup(); - - return 0; -} - -static int -test_allowed_permissions() -{ - int ret; - int numPerms; - - /* Make sure these fail before initialization. - */ - ret = countPermissionConflicts((PermissionRequestList *)1, false); - assert(ret < 0); - - ret = getAllowedPermissions((const char *)1, false, (unsigned int *)1); - assert(ret < 0); - - /* Initialize. - */ - ret = permissionInit(); - assert(ret == 0); - - /* Make sure countPermissionConflicts() fails with bad parameters. - */ - ret = countPermissionConflicts(NULL, false); - assert(ret < 0); - - /* Register a set of permissions. - */ - Permission perms[] = { - { "/", PERM_NONE }, - { "/stat", PERM_STAT }, - { "/read", PERMSET_READ }, - { "/write", PERMSET_WRITE }, - { "/.stat", PERM_STAT }, - { "/.stat/.read", PERMSET_READ }, - { "/.stat/.read/.write", PERMSET_WRITE }, - { "/.stat/.write", PERMSET_WRITE }, - }; - numPerms = sizeof(perms) / sizeof(perms[0]); - ret = registerPermissionSet(numPerms, perms); - assert(ret == 0); - - /* Build a permission request list. - */ - PermissionRequestList list; - ret = initPermissionRequestList(&list); - assert(ret == 0); - - ret = addPermissionRequestToList(&list, "/stat", false, PERM_STAT); - assert(ret == 0); - - ret = addPermissionRequestToList(&list, "/read", false, PERM_READ); - assert(ret == 0); - - ret = addPermissionRequestToList(&list, "/write", false, PERM_WRITE); - assert(ret == 0); - - //TODO: cover more cases once the permission stuff has been implemented - - /* All of the requests in the list should be allowed. - */ - ret = countPermissionConflicts(&list, false); - assert(ret == 0); - - /* Add a request that will be denied. - */ - ret = addPermissionRequestToList(&list, "/stat", false, 1<<31 | PERM_STAT); - assert(ret == 0); - - ret = countPermissionConflicts(&list, false); - assert(ret == 1); - - //TODO: more tests - - permissionCleanup(); - - return 0; -} - -int -test_permissions() -{ - int ret; - - ret = test_permission_list(); - if (ret != 0) { - fprintf(stderr, "test_permission_list() failed: %d\n", ret); - return ret; - } - - ret = test_permission_table(); - if (ret != 0) { - fprintf(stderr, "test_permission_table() failed: %d\n", ret); - return ret; - } - - ret = test_allowed_permissions(); - if (ret != 0) { - fprintf(stderr, "test_permission_table() failed: %d\n", ret); - return ret; - } - - return 0; -} diff --git a/commands.c b/commands.c index 23ad91c03..b4678ba6f 100644 --- a/commands.c +++ b/commands.c @@ -57,39 +57,13 @@ static int gDidShowProgress = 0; if (argc < 0) return -1; \ assert(argc == 0 || argv != NULL); \ if (argc != 0 && argv == NULL) return -1; \ - if (permissions != NULL) { \ - int CW_I_; \ - for (CW_I_ = 0; CW_I_ < argc; CW_I_++) { \ - assert(argv[CW_I_] != NULL); \ - if (argv[CW_I_] == NULL) return -1; \ - } \ - } \ } while (false) #define CHECK_FN() \ do { \ CHECK_WORDS(); \ - if (permissions != NULL) { \ - assert(result == NULL); \ - if (result != NULL) return -1; \ - } else { \ - assert(result != NULL); \ - if (result == NULL) return -1; \ - } \ - } while (false) - -#define NO_PERMS(perms) \ - do { \ - PermissionRequestList *NP_PRL_ = (perms); \ - if (NP_PRL_ != NULL) { \ - int NP_RET_ = addPermissionRequestToList(NP_PRL_, \ - "", false, PERM_NONE); \ - if (NP_RET_ < 0) { \ - /* Returns from the calling function. \ - */ \ - return NP_RET_; \ - } \ - } \ + assert(result != NULL); \ + if (result == NULL) return -1; \ } while (false) /* @@ -99,13 +73,11 @@ static int gDidShowProgress = 0; /* assert <boolexpr> */ static int -cmd_assert(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_assert(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); CHECK_BOOL(); - NO_PERMS(permissions); /* If our argument is false, return non-zero (failure) * If our argument is true, return zero (success) @@ -120,8 +92,7 @@ cmd_assert(const char *name, void *cookie, int argc, const char *argv[], /* format <root> */ static int -cmd_format(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_format(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); @@ -151,8 +122,7 @@ cmd_format(const char *name, void *cookie, int argc, const char *argv[], * give up early. */ static int -cmd_delete(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_delete(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(cookie); CHECK_WORDS(); @@ -166,7 +136,6 @@ cmd_delete(const char *name, void *cookie, int argc, const char *argv[], recurse = (strcmp(name, "delete_recursive") == 0); ui_print("Deleting files...\n"); -//xxx permissions int i; for (i = 0; i < argc; i++) { @@ -233,13 +202,11 @@ static void extract_cb(const char *fn, void *cookie) * or a fixed default timestamp will be supplied otherwise. */ static int -cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); CHECK_WORDS(); -//xxx permissions // To create a consistent system image, never use the clock for timestamps. struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default @@ -331,8 +298,7 @@ cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[], * Run an external program included in the update package. */ static int -cmd_run_program(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_run_program(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(cookie); CHECK_WORDS(); @@ -407,8 +373,7 @@ cmd_run_program(const char *name, void *cookie, int argc, const char *argv[], * User, group, and modes must all be integer values (hex or octal OK). */ static int -cmd_set_perm(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_set_perm(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(cookie); CHECK_WORDS(); @@ -461,8 +426,7 @@ cmd_set_perm(const char *name, void *cookie, int argc, const char *argv[], * if the actual rate of progress can be determined). */ static int -cmd_show_progress(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_show_progress(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(cookie); CHECK_WORDS(); @@ -499,8 +463,7 @@ cmd_show_progress(const char *name, void *cookie, int argc, const char *argv[], * for the target filesystem (and may be relative). */ static int -cmd_symlink(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_symlink(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(cookie); CHECK_WORDS(); @@ -554,7 +517,7 @@ static bool firmware_fn(const unsigned char *data, int data_len, void *cookie) */ static int cmd_write_firmware_image(const char *name, void *cookie, - int argc, const char *argv[], PermissionRequestList *permissions) + int argc, const char *argv[]) { UNUSED(cookie); CHECK_WORDS(); @@ -634,11 +597,10 @@ static bool write_raw_image_process_fn( */ static int cmd_write_raw_image(const char *name, void *cookie, - int argc, const char *argv[], PermissionRequestList *permissions) + int argc, const char *argv[]) { UNUSED(cookie); CHECK_WORDS(); -//xxx permissions if (argc != 2) { LOGE("Command %s requires exactly two arguments\n", name); @@ -726,8 +688,7 @@ cmd_write_raw_image(const char *name, void *cookie, /* mark <resource> dirty|clean */ static int -cmd_mark(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_mark(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); @@ -742,8 +703,7 @@ cmd_mark(const char *name, void *cookie, int argc, const char *argv[], /* done */ static int -cmd_done(const char *name, void *cookie, int argc, const char *argv[], - PermissionRequestList *permissions) +cmd_done(const char *name, void *cookie, int argc, const char *argv[]) { UNUSED(name); UNUSED(cookie); @@ -764,13 +724,11 @@ cmd_done(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_compatible_with(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc != 1) { fprintf(stderr, "%s: wrong number of arguments (%d)\n", @@ -796,13 +754,11 @@ fn_compatible_with(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_update_forced(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc != 0) { fprintf(stderr, "%s: wrong number of arguments (%d)\n", @@ -830,13 +786,11 @@ fn_update_forced(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_get_mark(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc != 1) { fprintf(stderr, "%s: wrong number of arguments (%d)\n", @@ -857,8 +811,7 @@ fn_get_mark(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { int ret = -1; @@ -875,24 +828,6 @@ fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[], dir = argv[0]; } - if (permissions != NULL) { - if (dir == NULL) { - /* The argument is the result of another function. - * Assume the worst case, where the function returns - * the root. - */ - dir = "/"; - } - ret = addPermissionRequestToList(permissions, dir, true, PERM_READ); - } else { -//xxx build and return the string - *result = strdup("hashvalue"); - if (resultLen != NULL) { - *resultLen = strlen(*result); - } - ret = 0; - } - return ret; } @@ -904,13 +839,11 @@ fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_matches(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc < 2) { fprintf(stderr, "%s: not enough arguments (%d < 2)\n", @@ -941,13 +874,11 @@ fn_matches(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_concat(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(name); UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); size_t totalLen = 0; int i; @@ -977,12 +908,10 @@ fn_concat(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_getprop(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc != 1) { LOGE("Command %s requires exactly one argument\n", name); @@ -1005,12 +934,10 @@ fn_getprop(const char *name, void *cookie, int argc, const char *argv[], */ static int fn_file_contains(const char *name, void *cookie, int argc, const char *argv[], - char **result, size_t *resultLen, - PermissionRequestList *permissions) + char **result, size_t *resultLen) { UNUSED(cookie); CHECK_FN(); - NO_PERMS(permissions); if (argc != 2) { LOGE("Command %s requires exactly two arguments\n", name); @@ -47,7 +47,6 @@ void ui_end_menu(); // Set the icon (normally the only thing visible besides the progress bar). enum { BACKGROUND_ICON_NONE, - BACKGROUND_ICON_UNPACKING, BACKGROUND_ICON_INSTALLING, BACKGROUND_ICON_ERROR, BACKGROUND_ICON_FIRMWARE_INSTALLING, @@ -91,4 +90,7 @@ void ui_reset_progress(); #define LOGD(...) do {} while (0) #endif +#define STRINGIFY(x) #x +#define EXPAND(x) STRINGIFY(x) + #endif // RECOVERY_COMMON_H diff --git a/edify/Android.mk b/edify/Android.mk new file mode 100644 index 000000000..fac0ba712 --- /dev/null +++ b/edify/Android.mk @@ -0,0 +1,39 @@ +# Copyright 2009 The Android Open Source Project + +LOCAL_PATH := $(call my-dir) + +edify_src_files := \ + lexer.l \ + parser.y \ + expr.c + +# "-x c" forces the lex/yacc files to be compiled as c; +# the build system otherwise forces them to be c++. +edify_cflags := -x c + +# +# Build the host-side command line tool +# +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + $(edify_src_files) \ + main.c + +LOCAL_CFLAGS := $(edify_cflags) -g -O0 +LOCAL_MODULE := edify +LOCAL_YACCFLAGS := -v + +include $(BUILD_HOST_EXECUTABLE) + +# +# Build the device-side library +# +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(edify_src_files) + +LOCAL_CFLAGS := $(edify_cflags) +LOCAL_MODULE := libedify + +include $(BUILD_STATIC_LIBRARY) diff --git a/edify/README b/edify/README new file mode 100644 index 000000000..810455cca --- /dev/null +++ b/edify/README @@ -0,0 +1,108 @@ +Update scripts (from donut onwards) are written in a new little +scripting language ("edify") that is superficially somewhat similar to +the old one ("amend"). This is a brief overview of the new language. + +- The entire script is a single expression. + +- All expressions are string-valued. + +- String literals appear in double quotes. \n, \t, \", and \\ are + understood, as are hexadecimal escapes like \x4a. + +- String literals consisting of only letters, numbers, colons, + underscores, slashes, and periods don't need to be in double quotes. + +- The following words are reserved: + + if then else endif + + They have special meaning when unquoted. (In quotes, they are just + string literals.) + +- When used as a boolean, the empty string is "false" and all other + strings are "true". + +- All functions are actually macros (in the Lisp sense); the body of + the function can control which (if any) of the arguments are + evaluated. This means that functions can act as control + structures. + +- Operators (like "&&" and "||") are just syntactic sugar for builtin + functions, so they can act as control structures as well. + +- ";" is a binary operator; evaluating it just means to first evaluate + the left side, then the right. It can also appear after any + expression. + +- Comments start with "#" and run to the end of the line. + + + +Some examples: + +- There's no distinction between quoted and unquoted strings; the + quotes are only needed if you want characters like whitespace to + appear in the string. The following expressions all evaluate to the + same string. + + "a b" + a + " " + b + "a" + " " + "b" + "a\x20b" + a + "\x20b" + concat(a, " ", "b") + "concat"(a, " ", "b") + + As shown in the last example, function names are just strings, + too. They must be string *literals*, however. This is not legal: + + ("con" + "cat")(a, " ", b) # syntax error! + + +- The ifelse() builtin takes three arguments: it evaluates exactly + one of the second and third, depending on whether the first one is + true. There is also some syntactic sugar to make expressions that + look like if/else statements: + + # these are all equivalent + ifelse(something(), "yes", "no") + if something() then yes else no endif + if something() then "yes" else "no" endif + + The else part is optional. + + if something() then "yes" endif # if something() is false, + # evaluates to false + + ifelse(condition(), "", abort()) # abort() only called if + # condition() is false + + The last example is equivalent to: + + assert(condition()) + + +- The && and || operators can be used similarly; they evaluate their + second argument only if it's needed to determine the truth of the + expression. Their value is the value of the last-evaluated + argument: + + file_exists("/data/system/bad") && delete("/data/system/bad") + + file_exists("/data/system/missing") || create("/data/system/missing") + + get_it() || "xxx" # returns value of get_it() if that value is + # true, otherwise returns "xxx" + + +- The purpose of ";" is to simulate imperative statements, of course, + but the operator can be used anywhere. Its value is the value of + its right side: + + concat(a;b;c, d, e;f) # evaluates to "cdf" + + A more useful example might be something like: + + ifelse(condition(), + (first_step(); second_step();), # second ; is optional + alternative_procedure()) diff --git a/edify/expr.c b/edify/expr.c new file mode 100644 index 000000000..72e5100f3 --- /dev/null +++ b/edify/expr.c @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> + +#include "expr.h" + +// Functions should: +// +// - return a malloc()'d string +// - if Evaluate() on any argument returns NULL, return NULL. + +int BooleanString(const char* s) { + return s[0] != '\0'; +} + +char* Evaluate(State* state, Expr* expr) { + return expr->fn(expr->name, state, expr->argc, expr->argv); +} + +char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc == 0) { + return strdup(""); + } + char** strings = malloc(argc * sizeof(char*)); + int i; + for (i = 0; i < argc; ++i) { + strings[i] = NULL; + } + char* result = NULL; + int length = 0; + for (i = 0; i < argc; ++i) { + strings[i] = Evaluate(state, argv[i]); + if (strings[i] == NULL) { + goto done; + } + length += strlen(strings[i]); + } + + result = malloc(length+1); + int p = 0; + for (i = 0; i < argc; ++i) { + strcpy(result+p, strings[i]); + p += strlen(strings[i]); + } + result[p] = '\0'; + + done: + for (i = 0; i < argc; ++i) { + free(strings[i]); + } + return result; +} + +char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2 && argc != 3) { + free(state->errmsg); + state->errmsg = strdup("ifelse expects 2 or 3 arguments"); + return NULL; + } + char* cond = Evaluate(state, argv[0]); + if (cond == NULL) { + return NULL; + } + + if (BooleanString(cond) == true) { + free(cond); + return Evaluate(state, argv[1]); + } else { + if (argc == 3) { + free(cond); + return Evaluate(state, argv[2]); + } else { + return cond; + } + } +} + +char* AbortFn(const char* name, State* state, int argc, Expr* argv[]) { + char* msg = NULL; + if (argc > 0) { + msg = Evaluate(state, argv[0]); + } + free(state->errmsg); + if (msg) { + state->errmsg = msg; + } else { + state->errmsg = strdup("called abort()"); + } + return NULL; +} + +char* AssertFn(const char* name, State* state, int argc, Expr* argv[]) { + int i; + for (i = 0; i < argc; ++i) { + char* v = Evaluate(state, argv[i]); + if (v == NULL) { + return NULL; + } + int b = BooleanString(v); + free(v); + if (!b) { + int prefix_len; + int len = argv[i]->end - argv[i]->start; + char* err_src = malloc(len + 20); + strcpy(err_src, "assert failed: "); + prefix_len = strlen(err_src); + memcpy(err_src + prefix_len, state->script + argv[i]->start, len); + err_src[prefix_len + len] = '\0'; + free(state->errmsg); + state->errmsg = err_src; + return NULL; + } + } + return strdup(""); +} + +char* SleepFn(const char* name, State* state, int argc, Expr* argv[]) { + char* val = Evaluate(state, argv[0]); + if (val == NULL) { + return NULL; + } + int v = strtol(val, NULL, 10); + sleep(v); + return val; +} + +char* StdoutFn(const char* name, State* state, int argc, Expr* argv[]) { + int i; + for (i = 0; i < argc; ++i) { + char* v = Evaluate(state, argv[i]); + if (v == NULL) { + return NULL; + } + fputs(v, stdout); + free(v); + } + return strdup(""); +} + +char* LogicalAndFn(const char* name, State* state, + int argc, Expr* argv[]) { + char* left = Evaluate(state, argv[0]); + if (left == NULL) return NULL; + if (BooleanString(left) == true) { + free(left); + return Evaluate(state, argv[1]); + } else { + return left; + } +} + +char* LogicalOrFn(const char* name, State* state, + int argc, Expr* argv[]) { + char* left = Evaluate(state, argv[0]); + if (left == NULL) return NULL; + if (BooleanString(left) == false) { + free(left); + return Evaluate(state, argv[1]); + } else { + return left; + } +} + +char* LogicalNotFn(const char* name, State* state, + int argc, Expr* argv[]) { + char* val = Evaluate(state, argv[0]); + if (val == NULL) return NULL; + bool bv = BooleanString(val); + free(val); + if (bv) { + return strdup(""); + } else { + return strdup("t"); + } +} + +char* SubstringFn(const char* name, State* state, + int argc, Expr* argv[]) { + char* needle = Evaluate(state, argv[0]); + if (needle == NULL) return NULL; + char* haystack = Evaluate(state, argv[1]); + if (haystack == NULL) { + free(needle); + return NULL; + } + + char* result = strdup(strstr(haystack, needle) ? "t" : ""); + free(needle); + free(haystack); + return result; +} + +char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]) { + char* left = Evaluate(state, argv[0]); + if (left == NULL) return NULL; + char* right = Evaluate(state, argv[1]); + if (right == NULL) { + free(left); + return NULL; + } + + char* result = strdup(strcmp(left, right) == 0 ? "t" : ""); + free(left); + free(right); + return result; +} + +char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]) { + char* left = Evaluate(state, argv[0]); + if (left == NULL) return NULL; + char* right = Evaluate(state, argv[1]); + if (right == NULL) { + free(left); + return NULL; + } + + char* result = strdup(strcmp(left, right) != 0 ? "t" : ""); + free(left); + free(right); + return result; +} + +char* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) { + char* left = Evaluate(state, argv[0]); + if (left == NULL) return NULL; + free(left); + return Evaluate(state, argv[1]); +} + +char* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + free(state->errmsg); + state->errmsg = strdup("less_than_int expects 2 arguments"); + return NULL; + } + + char* left; + char* right; + if (ReadArgs(state, argv, 2, &left, &right) < 0) return NULL; + + bool result = false; + char* end; + + long l_int = strtol(left, &end, 10); + if (left[0] == '\0' || *end != '\0') { + fprintf(stderr, "[%s] is not an int\n", left); + goto done; + } + + long r_int = strtol(right, &end, 10); + if (right[0] == '\0' || *end != '\0') { + fprintf(stderr, "[%s] is not an int\n", right); + goto done; + } + + result = l_int < r_int; + + done: + free(left); + free(right); + return strdup(result ? "t" : ""); +} + +char* GreaterThanIntFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + free(state->errmsg); + state->errmsg = strdup("greater_than_int expects 2 arguments"); + return NULL; + } + + Expr* temp[2]; + temp[0] = argv[1]; + temp[1] = argv[0]; + + return LessThanIntFn(name, state, 2, temp); +} + +char* Literal(const char* name, State* state, int argc, Expr* argv[]) { + return strdup(name); +} + +Expr* Build(Function fn, YYLTYPE loc, int count, ...) { + va_list v; + va_start(v, count); + Expr* e = malloc(sizeof(Expr)); + e->fn = fn; + e->name = "(operator)"; + e->argc = count; + e->argv = malloc(count * sizeof(Expr*)); + int i; + for (i = 0; i < count; ++i) { + e->argv[i] = va_arg(v, Expr*); + } + va_end(v); + e->start = loc.start; + e->end = loc.end; + return e; +} + +// ----------------------------------------------------------------- +// the function table +// ----------------------------------------------------------------- + +static int fn_entries = 0; +static int fn_size = 0; +NamedFunction* fn_table = NULL; + +void RegisterFunction(const char* name, Function fn) { + if (fn_entries >= fn_size) { + fn_size = fn_size*2 + 1; + fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction)); + } + fn_table[fn_entries].name = name; + fn_table[fn_entries].fn = fn; + ++fn_entries; +} + +static int fn_entry_compare(const void* a, const void* b) { + const char* na = ((const NamedFunction*)a)->name; + const char* nb = ((const NamedFunction*)b)->name; + return strcmp(na, nb); +} + +void FinishRegistration() { + qsort(fn_table, fn_entries, sizeof(NamedFunction), fn_entry_compare); +} + +Function FindFunction(const char* name) { + NamedFunction key; + key.name = name; + NamedFunction* nf = bsearch(&key, fn_table, fn_entries, + sizeof(NamedFunction), fn_entry_compare); + if (nf == NULL) { + return NULL; + } + return nf->fn; +} + +void RegisterBuiltins() { + RegisterFunction("ifelse", IfElseFn); + RegisterFunction("abort", AbortFn); + RegisterFunction("assert", AssertFn); + RegisterFunction("concat", ConcatFn); + RegisterFunction("is_substring", SubstringFn); + RegisterFunction("stdout", StdoutFn); + RegisterFunction("sleep", SleepFn); + + RegisterFunction("less_than_int", LessThanIntFn); + RegisterFunction("greater_than_int", GreaterThanIntFn); +} + + +// ----------------------------------------------------------------- +// convenience methods for functions +// ----------------------------------------------------------------- + +// Evaluate the expressions in argv, giving 'count' char* (the ... is +// zero or more char** to put them in). If any expression evaluates +// to NULL, free the rest and return -1. Return 0 on success. +int ReadArgs(State* state, Expr* argv[], int count, ...) { + char** args = malloc(count * sizeof(char*)); + va_list v; + va_start(v, count); + int i; + for (i = 0; i < count; ++i) { + args[i] = Evaluate(state, argv[i]); + if (args[i] == NULL) { + va_end(v); + int j; + for (j = 0; j < i; ++j) { + free(args[j]); + } + return -1; + } + *(va_arg(v, char**)) = args[i]; + } + va_end(v); + return 0; +} + +// Evaluate the expressions in argv, returning an array of char* +// results. If any evaluate to NULL, free the rest and return NULL. +// The caller is responsible for freeing the returned array and the +// strings it contains. +char** ReadVarArgs(State* state, int argc, Expr* argv[]) { + char** args = (char**)malloc(argc * sizeof(char*)); + int i = 0; + for (i = 0; i < argc; ++i) { + args[i] = Evaluate(state, argv[i]); + if (args[i] == NULL) { + int j; + for (j = 0; j < i; ++j) { + free(args[j]); + } + free(args); + return NULL; + } + } + return args; +} + +// Use printf-style arguments to compose an error message to put into +// *state. Returns NULL. +char* ErrorAbort(State* state, char* format, ...) { + char* buffer = malloc(4096); + va_list v; + va_start(v, format); + vsnprintf(buffer, 4096, format, v); + va_end(v); + free(state->errmsg); + state->errmsg = buffer; + return NULL; +} diff --git a/edify/expr.h b/edify/expr.h new file mode 100644 index 000000000..d2e739201 --- /dev/null +++ b/edify/expr.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _EXPRESSION_H +#define _EXPRESSION_H + +#include "yydefs.h" + +#define MAX_STRING_LEN 1024 + +typedef struct Expr Expr; + +typedef struct { + // Optional pointer to app-specific data; the core of edify never + // uses this value. + void* cookie; + + // The source of the original script. Must be NULL-terminated, + // and in writable memory (Evaluate may make temporary changes to + // it but will restore it when done). + char* script; + + // The error message (if any) returned if the evaluation aborts. + // Should be NULL initially, will be either NULL or a malloc'd + // pointer after Evaluate() returns. + char* errmsg; +} State; + +typedef char* (*Function)(const char* name, State* state, + int argc, Expr* argv[]); + +struct Expr { + Function fn; + char* name; + int argc; + Expr** argv; + int start, end; +}; + +char* Evaluate(State* state, Expr* expr); + +// Glue to make an Expr out of a literal. +char* Literal(const char* name, State* state, int argc, Expr* argv[]); + +// Functions corresponding to various syntactic sugar operators. +// ("concat" is also available as a builtin function, to concatenate +// more than two strings.) +char* ConcatFn(const char* name, State* state, int argc, Expr* argv[]); +char* LogicalAndFn(const char* name, State* state, int argc, Expr* argv[]); +char* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]); +char* LogicalNotFn(const char* name, State* state, int argc, Expr* argv[]); +char* SubstringFn(const char* name, State* state, int argc, Expr* argv[]); +char* EqualityFn(const char* name, State* state, int argc, Expr* argv[]); +char* InequalityFn(const char* name, State* state, int argc, Expr* argv[]); +char* SequenceFn(const char* name, State* state, int argc, Expr* argv[]); + +// Convenience function for building expressions with a fixed number +// of arguments. +Expr* Build(Function fn, YYLTYPE loc, int count, ...); + +// Global builtins, registered by RegisterBuiltins(). +char* IfElseFn(const char* name, State* state, int argc, Expr* argv[]); +char* AssertFn(const char* name, State* state, int argc, Expr* argv[]); +char* AbortFn(const char* name, State* state, int argc, Expr* argv[]); + + +// For setting and getting the global error string (when returning +// NULL from a function). +void SetError(const char* message); // makes a copy +const char* GetError(); // retains ownership +void ClearError(); + + +typedef struct { + const char* name; + Function fn; +} NamedFunction; + +// Register a new function. The same Function may be registered under +// multiple names, but a given name should only be used once. +void RegisterFunction(const char* name, Function fn); + +// Register all the builtins. +void RegisterBuiltins(); + +// Call this after all calls to RegisterFunction() but before parsing +// any scripts to finish building the function table. +void FinishRegistration(); + +// Find the Function for a given name; return NULL if no such function +// exists. +Function FindFunction(const char* name); + + +// --- convenience functions for use in functions --- + +// Evaluate the expressions in argv, giving 'count' char* (the ... is +// zero or more char** to put them in). If any expression evaluates +// to NULL, free the rest and return -1. Return 0 on success. +int ReadArgs(State* state, Expr* argv[], int count, ...); + +// Evaluate the expressions in argv, returning an array of char* +// results. If any evaluate to NULL, free the rest and return NULL. +// The caller is responsible for freeing the returned array and the +// strings it contains. +char** ReadVarArgs(State* state, int argc, Expr* argv[]); + +// Use printf-style arguments to compose an error message to put into +// *state. Returns NULL. +char* ErrorAbort(State* state, char* format, ...); + + +#endif // _EXPRESSION_H diff --git a/edify/lexer.l b/edify/lexer.l new file mode 100644 index 000000000..2c4489cc6 --- /dev/null +++ b/edify/lexer.l @@ -0,0 +1,110 @@ +%{ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "expr.h" +#include "yydefs.h" +#include "parser.h" + +int gLine = 1; +int gColumn = 1; +int gPos = 0; + +// TODO: enforce MAX_STRING_LEN during lexing +char string_buffer[MAX_STRING_LEN]; +char* string_pos; + +#define ADVANCE do {yylloc.start=gPos; yylloc.end=gPos+yyleng; \ + gColumn+=yyleng; gPos+=yyleng;} while(0) + +%} + +%x STR + +%option noyywrap + +%% + + +\" { + BEGIN(STR); + string_pos = string_buffer; + yylloc.start = gPos; + ++gColumn; + ++gPos; +} + +<STR>{ + \" { + ++gColumn; + ++gPos; + BEGIN(INITIAL); + *string_pos = '\0'; + yylval.str = strdup(string_buffer); + yylloc.end = gPos; + return STRING; + } + + \\n { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\n'; } + \\t { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\t'; } + \\\" { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\"'; } + \\\\ { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\\'; } + + \\x[0-9a-fA-F]{2} { + gColumn += yyleng; + gPos += yyleng; + int val; + sscanf(yytext+2, "%x", &val); + *string_pos++ = val; + } + + \n { + ++gLine; + ++gPos; + gColumn = 1; + *string_pos++ = yytext[0]; + } + + . { + ++gColumn; + ++gPos; + *string_pos++ = yytext[0]; + } +} + +if ADVANCE; return IF; +then ADVANCE; return THEN; +else ADVANCE; return ELSE; +endif ADVANCE; return ENDIF; + +[a-zA-Z0-9_:/.]+ { + ADVANCE; + yylval.str = strdup(yytext); + return STRING; +} + +\&\& ADVANCE; return AND; +\|\| ADVANCE; return OR; +== ADVANCE; return EQ; +!= ADVANCE; return NE; + +[+(),!;] ADVANCE; return yytext[0]; + +[ \t]+ ADVANCE; + +(#.*)?\n gPos += yyleng; ++gLine; gColumn = 1; + +. return BAD; diff --git a/edify/main.c b/edify/main.c new file mode 100644 index 000000000..0e3610847 --- /dev/null +++ b/edify/main.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "expr.h" +#include "parser.h" + +extern int yyparse(Expr** root, int* error_count); + +int expect(const char* expr_str, const char* expected, int* errors) { + Expr* e; + int error; + char* result; + + printf("."); + + yy_scan_string(expr_str); + int error_count = 0; + error = yyparse(&e, &error_count); + if (error > 0 || error_count > 0) { + fprintf(stderr, "error parsing \"%s\" (%d errors)\n", + expr_str, error_count); + ++*errors; + return 0; + } + + State state; + state.cookie = NULL; + state.script = expr_str; + state.errmsg = NULL; + + result = Evaluate(&state, e); + free(state.errmsg); + if (result == NULL && expected != NULL) { + fprintf(stderr, "error evaluating \"%s\"\n", expr_str); + ++*errors; + return 0; + } + + if (result == NULL && expected == NULL) { + return 1; + } + + if (strcmp(result, expected) != 0) { + fprintf(stderr, "evaluating \"%s\": expected \"%s\", got \"%s\"\n", + expr_str, expected, result); + ++*errors; + free(result); + return 0; + } + + free(result); + return 1; +} + +int test() { + int errors = 0; + + expect("a", "a", &errors); + expect("\"a\"", "a", &errors); + expect("\"\\x61\"", "a", &errors); + expect("# this is a comment\n" + " a\n" + " \n", + "a", &errors); + + + // sequence operator + expect("a; b; c", "c", &errors); + + // string concat operator + expect("a + b", "ab", &errors); + expect("a + \n \"b\"", "ab", &errors); + expect("a + b +\nc\n", "abc", &errors); + + // string concat function + expect("concat(a, b)", "ab", &errors); + expect("concat(a,\n \"b\")", "ab", &errors); + expect("concat(a + b,\nc,\"d\")", "abcd", &errors); + expect("\"concat\"(a + b,\nc,\"d\")", "abcd", &errors); + + // logical and + expect("a && b", "b", &errors); + expect("a && \"\"", "", &errors); + expect("\"\" && b", "", &errors); + expect("\"\" && \"\"", "", &errors); + expect("\"\" && abort()", "", &errors); // test short-circuiting + expect("t && abort()", NULL, &errors); + + // logical or + expect("a || b", "a", &errors); + expect("a || \"\"", "a", &errors); + expect("\"\" || b", "b", &errors); + expect("\"\" || \"\"", "", &errors); + expect("a || abort()", "a", &errors); // test short-circuiting + expect("\"\" || abort()", NULL, &errors); + + // logical not + expect("!a", "", &errors); + expect("! \"\"", "t", &errors); + expect("!!a", "t", &errors); + + // precedence + expect("\"\" == \"\" && b", "b", &errors); + expect("a + b == ab", "t", &errors); + expect("ab == a + b", "t", &errors); + expect("a + (b == ab)", "a", &errors); + expect("(ab == a) + b", "b", &errors); + + // substring function + expect("is_substring(cad, abracadabra)", "t", &errors); + expect("is_substring(abrac, abracadabra)", "t", &errors); + expect("is_substring(dabra, abracadabra)", "t", &errors); + expect("is_substring(cad, abracxadabra)", "", &errors); + expect("is_substring(abrac, axbracadabra)", "", &errors); + expect("is_substring(dabra, abracadabrxa)", "", &errors); + + // ifelse function + expect("ifelse(t, yes, no)", "yes", &errors); + expect("ifelse(!t, yes, no)", "no", &errors); + expect("ifelse(t, yes, abort())", "yes", &errors); + expect("ifelse(!t, abort(), no)", "no", &errors); + + // if "statements" + expect("if t then yes else no endif", "yes", &errors); + expect("if \"\" then yes else no endif", "no", &errors); + expect("if \"\" then yes endif", "", &errors); + expect("if \"\"; t then yes endif", "yes", &errors); + + // numeric comparisons + expect("less_than_int(3, 14)", "t", &errors); + expect("less_than_int(14, 3)", "", &errors); + expect("less_than_int(x, 3)", "", &errors); + expect("less_than_int(3, x)", "", &errors); + expect("greater_than_int(3, 14)", "", &errors); + expect("greater_than_int(14, 3)", "t", &errors); + expect("greater_than_int(x, 3)", "", &errors); + expect("greater_than_int(3, x)", "", &errors); + + printf("\n"); + + return errors; +} + +void ExprDump(int depth, Expr* n, char* script) { + printf("%*s", depth*2, ""); + char temp = script[n->end]; + script[n->end] = '\0'; + printf("%s %p (%d-%d) \"%s\"\n", + n->name == NULL ? "(NULL)" : n->name, n->fn, n->start, n->end, + script+n->start); + script[n->end] = temp; + int i; + for (i = 0; i < n->argc; ++i) { + ExprDump(depth+1, n->argv[i], script); + } +} + +int main(int argc, char** argv) { + RegisterBuiltins(); + FinishRegistration(); + + if (argc == 1) { + return test() != 0; + } + + FILE* f = fopen(argv[1], "r"); + char buffer[8192]; + int size = fread(buffer, 1, 8191, f); + fclose(f); + buffer[size] = '\0'; + + Expr* root; + int error_count = 0; + yy_scan_bytes(buffer, size); + int error = yyparse(&root, &error_count); + printf("parse returned %d; %d errors encountered\n", error, error_count); + if (error == 0 || error_count > 0) { + + ExprDump(0, root, buffer); + + State state; + state.cookie = NULL; + state.script = buffer; + state.errmsg = NULL; + + char* result = Evaluate(&state, root); + if (result == NULL) { + printf("result was NULL, message is: %s\n", + (state.errmsg == NULL ? "(NULL)" : state.errmsg)); + free(state.errmsg); + } else { + printf("result is [%s]\n", result); + } + } + return 0; +} diff --git a/edify/parser.y b/edify/parser.y new file mode 100644 index 000000000..3f9ade144 --- /dev/null +++ b/edify/parser.y @@ -0,0 +1,130 @@ +%{ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "expr.h" +#include "yydefs.h" +#include "parser.h" + +extern int gLine; +extern int gColumn; + +void yyerror(Expr** root, int* error_count, const char* s); +int yyparse(Expr** root, int* error_count); + +%} + +%locations + +%union { + char* str; + Expr* expr; + struct { + int argc; + Expr** argv; + } args; +} + +%token AND OR SUBSTR SUPERSTR EQ NE IF THEN ELSE ENDIF +%token <str> STRING BAD +%type <expr> expr +%type <args> arglist + +%parse-param {Expr** root} +%parse-param {int* error_count} +%error-verbose + +/* declarations in increasing order of precedence */ +%left ';' +%left ',' +%left OR +%left AND +%left EQ NE +%left '+' +%right '!' + +%% + +input: expr { *root = $1; } +; + +expr: STRING { + $$ = malloc(sizeof(Expr)); + $$->fn = Literal; + $$->name = $1; + $$->argc = 0; + $$->argv = NULL; + $$->start = @$.start; + $$->end = @$.end; +} +| '(' expr ')' { $$ = $2; $$->start=@$.start; $$->end=@$.end; } +| expr ';' { $$ = $1; $$->start=@1.start; $$->end=@1.end; } +| expr ';' expr { $$ = Build(SequenceFn, @$, 2, $1, $3); } +| error ';' expr { $$ = $3; $$->start=@$.start; $$->end=@$.end; } +| expr '+' expr { $$ = Build(ConcatFn, @$, 2, $1, $3); } +| expr EQ expr { $$ = Build(EqualityFn, @$, 2, $1, $3); } +| expr NE expr { $$ = Build(InequalityFn, @$, 2, $1, $3); } +| expr AND expr { $$ = Build(LogicalAndFn, @$, 2, $1, $3); } +| expr OR expr { $$ = Build(LogicalOrFn, @$, 2, $1, $3); } +| '!' expr { $$ = Build(LogicalNotFn, @$, 1, $2); } +| IF expr THEN expr ENDIF { $$ = Build(IfElseFn, @$, 2, $2, $4); } +| IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, @$, 3, $2, $4, $6); } +| STRING '(' arglist ')' { + $$ = malloc(sizeof(Expr)); + $$->fn = FindFunction($1); + if ($$->fn == NULL) { + char buffer[256]; + snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1); + yyerror(root, error_count, buffer); + YYERROR; + } + $$->name = $1; + $$->argc = $3.argc; + $$->argv = $3.argv; + $$->start = @$.start; + $$->end = @$.end; +} +; + +arglist: /* empty */ { + $$.argc = 0; + $$.argv = NULL; +} +| expr { + $$.argc = 1; + $$.argv = malloc(sizeof(Expr*)); + $$.argv[0] = $1; +} +| arglist ',' expr { + $$.argc = $1.argc + 1; + $$.argv = realloc($$.argv, $$.argc * sizeof(Expr*)); + $$.argv[$$.argc-1] = $3; +} +; + +%% + +void yyerror(Expr** root, int* error_count, const char* s) { + if (strlen(s) == 0) { + s = "syntax error"; + } + printf("line %d col %d: %s\n", gLine, gColumn, s); + ++*error_count; +} diff --git a/edify/yydefs.h b/edify/yydefs.h new file mode 100644 index 000000000..625786255 --- /dev/null +++ b/edify/yydefs.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _YYDEFS_H_ +#define _YYDEFS_H_ + +#define YYLTYPE YYLTYPE +typedef struct { + int start, end; +} YYLTYPE; + +#define YYLLOC_DEFAULT(Current, Rhs, N) \ + do { \ + if (N) { \ + (Current).start = YYRHSLOC(Rhs, 1).start; \ + (Current).end = YYRHSLOC(Rhs, N).end; \ + } else { \ + (Current).start = YYRHSLOC(Rhs, 0).start; \ + (Current).end = YYRHSLOC(Rhs, 0).end; \ + } \ + } while (0) + +#endif diff --git a/firmware.c b/firmware.c index 34b291835..e2e4fe630 100644 --- a/firmware.c +++ b/firmware.c @@ -39,6 +39,10 @@ int remember_firmware_update(const char *type, const char *data, int length) { return 0; } +// Return true if there is a firmware update pending. +int firmware_update_pending() { + return update_data != NULL && update_length > 0; +} /* Bootloader / Recovery Flow * diff --git a/firmware.h b/firmware.h index f3f7aab79..aeb8f97aa 100644 --- a/firmware.h +++ b/firmware.h @@ -23,6 +23,9 @@ */ int remember_firmware_update(const char *type, const char *data, int length); +/* Returns true if a firmware update has been saved. */ +int firmware_update_pending(); + /* If an update was saved, reboot into the bootloader now to install it. * Returns 0 if no radio image was defined, nonzero on error, * doesn't return at all on success... @@ -14,10 +14,13 @@ * limitations under the License. */ +#include <ctype.h> #include <errno.h> #include <fcntl.h> #include <limits.h> #include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> #include "amend/amend.h" #include "common.h" @@ -30,13 +33,11 @@ #include "mtdutils/mtdutils.h" #include "roots.h" #include "verifier.h" - -/* List of public keys */ -static const RSAPublicKey keys[] = { -#include "keys.inc" -}; +#include "firmware.h" #define ASSUMED_UPDATE_SCRIPT_NAME "META-INF/com/google/android/update-script" +#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" +#define PUBLIC_KEYS_FILE "/res/keys" static const ZipEntry * find_update_script(ZipArchive *zip) @@ -99,7 +100,7 @@ handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) int ret = execCommandList((ExecContext *)1, commands); if (ret != 0) { int num = ret; - char *line, *next = script_data; + char *line = NULL, *next = script_data; while (next != NULL && ret-- > 0) { line = next; next = memchr(line, '\n', script_data + script_len - line); @@ -109,12 +110,211 @@ handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) return INSTALL_ERROR; } - ui_print("Installation complete.\n"); + LOGI("Installation complete.\n"); return INSTALL_SUCCESS; } +// The update binary ask us to install a firmware file on reboot. Set +// that up. Takes ownership of type and filename. static int -handle_update_package(const char *path, ZipArchive *zip) +handle_firmware_update(char* type, char* filename, ZipArchive* zip) { + unsigned int data_size; + const ZipEntry* entry = NULL; + + if (strncmp(filename, "PACKAGE:", 8) == 0) { + entry = mzFindZipEntry(zip, filename+8); + if (entry == NULL) { + LOGE("Failed to find \"%s\" in package", filename+8); + return INSTALL_ERROR; + } + data_size = entry->uncompLen; + } else { + struct stat st_data; + if (stat(filename, &st_data) < 0) { + LOGE("Error stat'ing %s: %s\n", filename, strerror(errno)); + return INSTALL_ERROR; + } + data_size = st_data.st_size; + } + + LOGI("type is %s; size is %d; file is %s\n", + type, data_size, filename); + + char* data = malloc(data_size); + if (data == NULL) { + LOGI("Can't allocate %d bytes for firmware data\n", data_size); + return INSTALL_ERROR; + } + + if (entry) { + if (mzReadZipEntry(zip, entry, data, data_size) == false) { + LOGE("Failed to read \"%s\" from package", filename+8); + return INSTALL_ERROR; + } + } else { + FILE* f = fopen(filename, "rb"); + if (f == NULL) { + LOGE("Failed to open %s: %s\n", filename, strerror(errno)); + return INSTALL_ERROR; + } + if (fread(data, 1, data_size, f) != data_size) { + LOGE("Failed to read firmware data: %s\n", strerror(errno)); + return INSTALL_ERROR; + } + fclose(f); + } + + if (remember_firmware_update(type, data, data_size)) { + LOGE("Can't store %s image\n", type); + free(data); + return INSTALL_ERROR; + } + free(filename); + + return INSTALL_SUCCESS; +} + +// If the package contains an update binary, extract it and run it. +static int +try_update_binary(const char *path, ZipArchive *zip) { + const ZipEntry* binary_entry = + mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); + if (binary_entry == NULL) { + return INSTALL_CORRUPT; + } + + char* binary = "/tmp/update_binary"; + unlink(binary); + int fd = creat(binary, 0755); + if (fd < 0) { + LOGE("Can't make %s\n", binary); + return 1; + } + bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd); + close(fd); + + if (!ok) { + LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME); + return 1; + } + + int pipefd[2]; + pipe(pipefd); + + // When executing the update binary contained in the package, the + // arguments passed are: + // + // - the version number for this interface + // + // - an fd to which the program can write in order to update the + // progress bar. The program can write single-line commands: + // + // progress <frac> <secs> + // fill up the next <frac> part of of the progress bar + // over <secs> seconds. If <secs> is zero, use + // set_progress commands to manually control the + // progress of this segment of the bar + // + // set_progress <frac> + // <frac> should be between 0.0 and 1.0; sets the + // progress bar within the segment defined by the most + // recent progress command. + // + // firmware <"hboot"|"radio"> <filename> + // arrange to install the contents of <filename> in the + // given partition on reboot. (API v2: <filename> may + // start with "PACKAGE:" to indicate taking a file from + // the OTA package.) + // + // ui_print <string> + // display <string> on the screen. + // + // - the name of the package zip file. + // + + char** args = malloc(sizeof(char*) * 5); + args[0] = binary; + args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk + args[2] = malloc(10); + sprintf(args[2], "%d", pipefd[1]); + args[3] = (char*)path; + args[4] = NULL; + + pid_t pid = fork(); + if (pid == 0) { + close(pipefd[0]); + execv(binary, args); + fprintf(stderr, "E:Can't run %s (%s)\n", binary, strerror(errno)); + _exit(-1); + } + close(pipefd[1]); + + char* firmware_type = NULL; + char* firmware_filename = NULL; + + char buffer[81]; + FILE* from_child = fdopen(pipefd[0], "r"); + while (fgets(buffer, sizeof(buffer), from_child) != NULL) { + LOGI("read: %s", buffer); + + char* command = strtok(buffer, " \n"); + if (command == NULL) { + continue; + } else if (strcmp(command, "progress") == 0) { + char* fraction_s = strtok(NULL, " \n"); + char* seconds_s = strtok(NULL, " \n"); + + float fraction = strtof(fraction_s, NULL); + int seconds = strtol(seconds_s, NULL, 10); + + ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), + seconds); + } else if (strcmp(command, "set_progress") == 0) { + char* fraction_s = strtok(NULL, " \n"); + float fraction = strtof(fraction_s, NULL); + ui_set_progress(fraction); + } else if (strcmp(command, "firmware") == 0) { + char* type = strtok(NULL, " \n"); + char* filename = strtok(NULL, " \n"); + + if (type != NULL && filename != NULL) { + if (firmware_type != NULL) { + LOGE("ignoring attempt to do multiple firmware updates"); + } else { + firmware_type = strdup(type); + firmware_filename = strdup(filename); + } + } + } else if (strcmp(command, "ui_print") == 0) { + char* str = strtok(NULL, "\n"); + if (str) { + ui_print(str); + } else { + ui_print("\n"); + } + } else { + LOGE("unknown command [%s]\n", command); + } + } + fclose(from_child); + + int status; + waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status)); + return INSTALL_ERROR; + } + + if (firmware_type != NULL) { + return handle_firmware_update(firmware_type, firmware_filename, zip); + } else { + return INSTALL_SUCCESS; + } +} + +static int +handle_update_package(const char *path, ZipArchive *zip, + const RSAPublicKey *keys, int numKeys) { // Give verification half the progress bar... ui_print("Verifying update package...\n"); @@ -122,7 +322,7 @@ handle_update_package(const char *path, ZipArchive *zip) VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME); - if (!verify_jar_signature(zip, keys, sizeof(keys) / sizeof(keys[0]))) { + if (!verify_jar_signature(zip, keys, numKeys)) { LOGE("Verification failed\n"); return INSTALL_CORRUPT; } @@ -130,6 +330,16 @@ handle_update_package(const char *path, ZipArchive *zip) // Update should take the rest of the progress bar. ui_print("Installing update...\n"); + int result = try_update_binary(path, zip); + if (result == INSTALL_SUCCESS || result == INSTALL_ERROR) { + register_package_root(NULL, NULL); // Unregister package root + return result; + } + + // if INSTALL_CORRUPT is returned, this package doesn't have an + // update binary. Fall back to the older mechanism of looking for + // an update script. + const ZipEntry *script_entry; script_entry = find_update_script(zip); if (script_entry == NULL) { @@ -147,6 +357,80 @@ handle_update_package(const char *path, ZipArchive *zip) return ret; } +// Reads a file containing one or more public keys as produced by +// DumpPublicKey: this is an RSAPublicKey struct as it would appear +// as a C source literal, eg: +// +// "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}" +// +// (Note that the braces and commas in this example are actual +// characters the parser expects to find in the file; the ellipses +// indicate more numbers omitted from this example.) +// +// The file may contain multiple keys in this format, separated by +// commas. The last key must not be followed by a comma. +// +// Returns NULL if the file failed to parse, or if it contain zero keys. +static RSAPublicKey* +load_keys(const char* filename, int* numKeys) { + RSAPublicKey* out = NULL; + *numKeys = 0; + + FILE* f = fopen(filename, "r"); + if (f == NULL) { + LOGE("opening %s: %s\n", filename, strerror(errno)); + goto exit; + } + + int i; + bool done = false; + while (!done) { + ++*numKeys; + out = realloc(out, *numKeys * sizeof(RSAPublicKey)); + RSAPublicKey* key = out + (*numKeys - 1); + if (fscanf(f, " { %i , %i , { %i", + &(key->len), &(key->n0inv), &(key->n[0])) != 3) { + goto exit; + } + if (key->len != RSANUMWORDS) { + LOGE("key length (%d) does not match expected size\n", key->len); + goto exit; + } + for (i = 1; i < key->len; ++i) { + if (fscanf(f, " , %i", &(key->n[i])) != 1) goto exit; + } + if (fscanf(f, " } , { %i", &(key->rr[0])) != 1) goto exit; + for (i = 1; i < key->len; ++i) { + if (fscanf(f, " , %i", &(key->rr[i])) != 1) goto exit; + } + fscanf(f, " } } "); + + // if the line ends in a comma, this file has more keys. + switch (fgetc(f)) { + case ',': + // more keys to come. + break; + + case EOF: + done = true; + break; + + default: + LOGE("unexpected character between keys\n"); + goto exit; + } + } + + fclose(f); + return out; + +exit: + if (f) fclose(f); + free(out); + *numKeys = 0; + return NULL; +} + int install_package(const char *root_path) { @@ -169,6 +453,14 @@ install_package(const char *root_path) ui_print("Opening update package...\n"); LOGI("Update file path: %s\n", path); + int numKeys; + RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); + if (loadedKeys == NULL) { + LOGE("Failed to load keys\n"); + return INSTALL_CORRUPT; + } + LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE); + /* Try to open the package. */ ZipArchive zip; @@ -180,7 +472,8 @@ install_package(const char *root_path) /* Verify and install the contents of the package. */ - int status = handle_update_package(path, &zip); + int status = handle_update_package(path, &zip, loadedKeys, numKeys); mzCloseZipArchive(&zip); + free(loadedKeys); return status; } diff --git a/minui/resources.c b/minui/resources.c index 5beb6a6d9..7ecfeefce 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -29,97 +29,84 @@ #include <pixelflinger/pixelflinger.h> +#include <png.h> + #include "minui.h" -// File signature for BMP files. -// The letters 'BM' as a little-endian unsigned short. - -#define BMP_SIGNATURE 0x4d42 - -typedef struct { - // constant, value should equal BMP_SIGNATURE - unsigned short bfType; - // size of the file in bytes. - unsigned long bfSize; - // must always be set to zero. - unsigned short bfReserved1; - // must always be set to zero. - unsigned short bfReserved2; - // offset from the beginning of the file to the bitmap data. - unsigned long bfOffBits; - - // The BITMAPINFOHEADER: - // size of the BITMAPINFOHEADER structure, in bytes. - unsigned long biSize; - // width of the image, in pixels. - unsigned long biWidth; - // height of the image, in pixels. - unsigned long biHeight; - // number of planes of the target device, must be set to 1. - unsigned short biPlanes; - // number of bits per pixel. - unsigned short biBitCount; - // type of compression, zero means no compression. - unsigned long biCompression; - // size of the image data, in bytes. If there is no compression, - // it is valid to set this member to zero. - unsigned long biSizeImage; - // horizontal pixels per meter on the designated targer device, - // usually set to zero. - unsigned long biXPelsPerMeter; - // vertical pixels per meter on the designated targer device, - // usually set to zero. - unsigned long biYPelsPerMeter; - // number of colors used in the bitmap, if set to zero the - // number of colors is calculated using the biBitCount member. - unsigned long biClrUsed; - // number of color that are 'important' for the bitmap, - // if set to zero, all colors are important. - unsigned long biClrImportant; -} __attribute__((packed)) BitMapFileHeader; +// libpng gives "undefined reference to 'pow'" errors, and I have no +// idea how to convince the build system to link with -lm. We don't +// need this functionality (it's used for gamma adjustment) so provide +// a dummy implementation to satisfy the linker. +double pow(double x, double y) { + return x; +} int res_create_surface(const char* name, gr_surface* pSurface) { char resPath[256]; - BitMapFileHeader header; GGLSurface* surface = NULL; int result = 0; - - snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.bmp", name); + unsigned char header[8]; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + + snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name); resPath[sizeof(resPath)-1] = '\0'; - int fd = open(resPath, O_RDONLY); - if (fd == -1) { + FILE* fp = fopen(resPath, "rb"); + if (fp == NULL) { result = -1; goto exit; } - size_t bytesRead = read(fd, &header, sizeof(header)); + + size_t bytesRead = fread(header, 1, sizeof(header), fp); if (bytesRead != sizeof(header)) { result = -2; goto exit; } - if (header.bfType != BMP_SIGNATURE) { - result = -3; // Not a legal header + + if (png_sig_cmp(header, 0, sizeof(header))) { + result = -3; goto exit; } - if (header.biPlanes != 1) { + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { result = -4; goto exit; } - if (!(header.biBitCount == 24 || header.biBitCount == 32)) { + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { result = -5; goto exit; } - if (header.biCompression != 0) { + + if (setjmp(png_jmpbuf(png_ptr))) { result = -6; goto exit; } - size_t width = header.biWidth; - size_t height = header.biHeight; + + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, sizeof(header)); + png_read_info(png_ptr, info_ptr); + + size_t width = info_ptr->width; + size_t height = info_ptr->height; size_t stride = 4 * width; size_t pixelSize = stride * height; - + + int color_type = info_ptr->color_type; + int bit_depth = info_ptr->bit_depth; + int channels = info_ptr->channels; + if (bit_depth != 8 || (channels != 3 && channels != 4) || + (color_type != PNG_COLOR_TYPE_RGB && + color_type != PNG_COLOR_TYPE_RGBA)) { + return -7; + goto exit; + } + surface = malloc(sizeof(GGLSurface) + pixelSize); if (surface == NULL) { - result = -7; + result = -8; goto exit; } unsigned char* pData = (unsigned char*) (surface + 1); @@ -128,63 +115,43 @@ int res_create_surface(const char* name, gr_surface* pSurface) { surface->height = height; surface->stride = width; /* Yes, pixels, not bytes */ surface->data = pData; - surface->format = (header.biBitCount == 24) ? + surface->format = (channels == 3) ? GGL_PIXEL_FORMAT_RGBX_8888 : GGL_PIXEL_FORMAT_RGBA_8888; - // Source pixel bytes are stored B G R {A} - - lseek(fd, header.bfOffBits, SEEK_SET); - size_t y; - if (header.biBitCount == 24) { // RGB - size_t inputStride = (((3 * width + 3) >> 2) << 2); - for (y = 0; y < height; y++) { - unsigned char* pRow = pData + (height - (y + 1)) * stride; - bytesRead = read(fd, pRow, inputStride); - if (bytesRead != inputStride) { - result = -8; - goto exit; - } + int y; + if (channels == 3) { + for (y = 0; y < height; ++y) { + unsigned char* pRow = pData + y * stride; + png_read_row(png_ptr, pRow, NULL); + int x; for(x = width - 1; x >= 0; x--) { int sx = x * 3; int dx = x * 4; - unsigned char b = pRow[sx]; + unsigned char r = pRow[sx]; unsigned char g = pRow[sx + 1]; - unsigned char r = pRow[sx + 2]; + unsigned char b = pRow[sx + 2]; unsigned char a = 0xff; pRow[dx ] = r; // r pRow[dx + 1] = g; // g - pRow[dx + 2] = b; // b; + pRow[dx + 2] = b; // b pRow[dx + 3] = a; } } - } else { // RGBA - for (y = 0; y < height; y++) { - unsigned char* pRow = pData + (height - (y + 1)) * stride; - bytesRead = read(fd, pRow, stride); - if (bytesRead != stride) { - result = -9; - goto exit; - } - size_t x; - for(x = 0; x < width; x++) { - size_t xx = x * 4; - unsigned char b = pRow[xx]; - unsigned char g = pRow[xx + 1]; - unsigned char r = pRow[xx + 2]; - unsigned char a = pRow[xx + 3]; - pRow[xx ] = r; - pRow[xx + 1] = g; - pRow[xx + 2] = b; - pRow[xx + 3] = a; - } + } else { + for (y = 0; y < height; ++y) { + unsigned char* pRow = pData + y * stride; + png_read_row(png_ptr, pRow, NULL); } } + *pSurface = (gr_surface) surface; exit: - if (fd >= 0) { - close(fd); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + if (fp != NULL) { + fclose(fp); } if (result < 0) { if (surface) { diff --git a/minzip/Zip.c b/minzip/Zip.c index 100c833fe..8cdb89874 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -41,7 +41,7 @@ enum { CENSIZ = 20, CENLEN = 24, CENNAM = 28, - CENEXT = 30, + CENEXT = 30, CENCOM = 32, CENDSK = 34, CENATT = 36, @@ -66,13 +66,13 @@ enum { LOCSIG = 0x04034b50, // PK34 LOCHDR = 30, - + LOCVER = 4, LOCFLG = 6, LOCHOW = 8, LOCTIM = 10, LOCCRC = 14, - LOCSIZ = 18, + LOCSIZ = 18, LOCLEN = 22, LOCNAM = 26, LOCEXT = 28, @@ -757,7 +757,7 @@ bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry, { CopyProcessArgs args; bool ret; - + args.buf = buf; args.bufLen = bufLen; ret = mzProcessZipEntryContents(pArchive, pEntry, copyProcessFunction, @@ -770,15 +770,29 @@ bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry, } static bool writeProcessFunction(const unsigned char *data, int dataLen, - void *fd) + void *cookie) { - ssize_t n = write((int)fd, data, dataLen); - if (n != dataLen) { - LOGE("Can't write %d bytes (only %ld) from zip file: %s\n", - dataLen, n, strerror(errno)); - return false; + int fd = (int)cookie; + + ssize_t soFar = 0; + while (true) { + ssize_t n = write(fd, data+soFar, dataLen-soFar); + if (n <= 0) { + LOGE("Error writing %ld bytes from zip file from %p: %s\n", + dataLen-soFar, data+soFar, strerror(errno)); + if (errno != EINTR) { + return false; + } + } else if (n > 0) { + soFar += n; + if (soFar == dataLen) return true; + if (soFar > dataLen) { + LOGE("write overrun? (%ld bytes instead of %d)\n", + soFar, dataLen); + return false; + } + } } - return true; } /* @@ -788,7 +802,7 @@ bool mzExtractZipEntryToFile(const ZipArchive *pArchive, const ZipEntry *pEntry, int fd) { bool ret = mzProcessZipEntryContents(pArchive, pEntry, writeProcessFunction, - (void *)fd); + (void*)fd); if (!ret) { LOGE("Can't extract entry to file.\n"); return false; diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c index 2b0106f14..fc067669b 100644 --- a/mtdutils/mtdutils.c +++ b/mtdutils/mtdutils.c @@ -297,7 +297,14 @@ static int read_block(const MtdPartition *partition, int fd, char *data) after.corrected - before.corrected, after.failed - before.failed, pos); } else { - return 0; // Success! + int i; + for (i = 0; i < size; ++i) { + if (data[i] != 0) { + return 0; // Success! + } + } + fprintf(stderr, "mtd: read all-zero block at 0x%08lx; skipping\n", + pos); } pos += partition->erase_size; @@ -326,6 +333,10 @@ ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len) read += ctx->partition->erase_size; } + if (read >= len) { + return read; + } + // Read the next block into the buffer if (ctx->consumed == ctx->partition->erase_size && read < (int) len) { if (read_block(ctx->partition, ctx->fd, ctx->buffer)) return -1; diff --git a/recovery.c b/recovery.c index 221ee2975..5ccd38f2c 100644 --- a/recovery.c +++ b/recovery.c @@ -265,7 +265,6 @@ test_amend() { extern int test_symtab(void); extern int test_cmd_fn(void); - extern int test_permissions(void); int ret; LOGD("Testing symtab...\n"); ret = test_symtab(); @@ -273,9 +272,6 @@ test_amend() LOGD("Testing cmd_fn...\n"); ret = test_cmd_fn(); LOGD(" returned %d\n", ret); - LOGD("Testing permissions...\n"); - ret = test_permissions(); - LOGD(" returned %d\n", ret); } #endif // TEST_AMEND @@ -291,7 +287,8 @@ erase_root(const char *root) static void prompt_and_wait() { - char* headers[] = { "Android system recovery utility", + char* headers[] = { "Android system recovery <" + EXPAND(RECOVERY_API_VERSION) ">", "", "Use trackball to highlight;", "click to select.", @@ -302,9 +299,11 @@ prompt_and_wait() #define ITEM_REBOOT 0 #define ITEM_APPLY_SDCARD 1 #define ITEM_WIPE_DATA 2 +#define ITEM_WIPE_CACHE 3 char* items[] = { "reboot system now [Home+Back]", "apply sdcard:update.zip [Alt+S]", "wipe data/factory reset [Alt+W]", + "wipe cache partition", NULL }; ui_start_menu(headers, items); @@ -357,6 +356,13 @@ prompt_and_wait() if (!ui_text_visible()) return; break; + case ITEM_WIPE_CACHE: + ui_print("\n-- Wiping cache...\n"); + erase_root("CACHE:"); + ui_print("Cache wipe complete.\n"); + if (!ui_text_visible()) return; + break; + case ITEM_APPLY_SDCARD: ui_print("\n-- Install from sdcard...\n"); int status = install_package(SDCARD_PACKAGE_FILE); @@ -366,7 +372,12 @@ prompt_and_wait() } else if (!ui_text_visible()) { return; // reboot if logs aren't visible } else { - ui_print("Install from sdcard complete.\n"); + if (firmware_update_pending()) { + ui_print("\nReboot via home+back or menu\n" + "to complete installation.\n"); + } else { + ui_print("\nInstall from sdcard complete.\n"); + } } break; } diff --git a/res/images/icon_error.bmp b/res/images/icon_error.bmp Binary files differdeleted file mode 100644 index 7eb2bbc77..000000000 --- a/res/images/icon_error.bmp +++ /dev/null diff --git a/res/images/icon_error.png b/res/images/icon_error.png Binary files differnew file mode 100644 index 000000000..7064c2e23 --- /dev/null +++ b/res/images/icon_error.png diff --git a/res/images/icon_firmware_error.bmp b/res/images/icon_firmware_error.bmp Binary files differdeleted file mode 100644 index 5b8649f17..000000000 --- a/res/images/icon_firmware_error.bmp +++ /dev/null diff --git a/res/images/icon_firmware_error.png b/res/images/icon_firmware_error.png Binary files differnew file mode 100644 index 000000000..0c32c9ede --- /dev/null +++ b/res/images/icon_firmware_error.png diff --git a/res/images/icon_firmware_install.bmp b/res/images/icon_firmware_install.bmp Binary files differdeleted file mode 100644 index b0f5f959b..000000000 --- a/res/images/icon_firmware_install.bmp +++ /dev/null diff --git a/res/images/icon_firmware_install.png b/res/images/icon_firmware_install.png Binary files differnew file mode 100644 index 000000000..ee2afac5d --- /dev/null +++ b/res/images/icon_firmware_install.png diff --git a/res/images/icon_installing.bmp b/res/images/icon_installing.bmp Binary files differdeleted file mode 100644 index fff99fd7e..000000000 --- a/res/images/icon_installing.bmp +++ /dev/null diff --git a/res/images/icon_installing.png b/res/images/icon_installing.png Binary files differnew file mode 100644 index 000000000..f24f2e33f --- /dev/null +++ b/res/images/icon_installing.png diff --git a/res/images/icon_unpacking.bmp b/res/images/icon_unpacking.bmp Binary files differdeleted file mode 100644 index ab6548c58..000000000 --- a/res/images/icon_unpacking.bmp +++ /dev/null diff --git a/res/images/indeterminate1.bmp b/res/images/indeterminate1.bmp Binary files differdeleted file mode 100644 index 716c92568..000000000 --- a/res/images/indeterminate1.bmp +++ /dev/null diff --git a/res/images/indeterminate1.png b/res/images/indeterminate1.png Binary files differnew file mode 100644 index 000000000..264bf27e5 --- /dev/null +++ b/res/images/indeterminate1.png diff --git a/res/images/indeterminate2.bmp b/res/images/indeterminate2.bmp Binary files differdeleted file mode 100644 index 223cd3c1e..000000000 --- a/res/images/indeterminate2.bmp +++ /dev/null diff --git a/res/images/indeterminate2.png b/res/images/indeterminate2.png Binary files differnew file mode 100644 index 000000000..c30c049ab --- /dev/null +++ b/res/images/indeterminate2.png diff --git a/res/images/indeterminate3.bmp b/res/images/indeterminate3.bmp Binary files differdeleted file mode 100644 index fd9086a1f..000000000 --- a/res/images/indeterminate3.bmp +++ /dev/null diff --git a/res/images/indeterminate3.png b/res/images/indeterminate3.png Binary files differnew file mode 100644 index 000000000..891a00095 --- /dev/null +++ b/res/images/indeterminate3.png diff --git a/res/images/indeterminate4.bmp b/res/images/indeterminate4.bmp Binary files differdeleted file mode 100644 index 87b264034..000000000 --- a/res/images/indeterminate4.bmp +++ /dev/null diff --git a/res/images/indeterminate4.png b/res/images/indeterminate4.png Binary files differnew file mode 100644 index 000000000..7a6415149 --- /dev/null +++ b/res/images/indeterminate4.png diff --git a/res/images/indeterminate5.bmp b/res/images/indeterminate5.bmp Binary files differdeleted file mode 100644 index e16efb04c..000000000 --- a/res/images/indeterminate5.bmp +++ /dev/null diff --git a/res/images/indeterminate5.png b/res/images/indeterminate5.png Binary files differnew file mode 100644 index 000000000..cd6ab20a7 --- /dev/null +++ b/res/images/indeterminate5.png diff --git a/res/images/indeterminate6.bmp b/res/images/indeterminate6.bmp Binary files differdeleted file mode 100644 index 085ad951a..000000000 --- a/res/images/indeterminate6.bmp +++ /dev/null diff --git a/res/images/indeterminate6.png b/res/images/indeterminate6.png Binary files differnew file mode 100644 index 000000000..ddd9e7384 --- /dev/null +++ b/res/images/indeterminate6.png diff --git a/res/images/progress_bar_empty.bmp b/res/images/progress_bar_empty.bmp Binary files differdeleted file mode 100644 index 8e512fd92..000000000 --- a/res/images/progress_bar_empty.bmp +++ /dev/null diff --git a/res/images/progress_bar_empty.png b/res/images/progress_bar_empty.png Binary files differnew file mode 100644 index 000000000..9013f04ac --- /dev/null +++ b/res/images/progress_bar_empty.png diff --git a/res/images/progress_bar_empty_left_round.bmp b/res/images/progress_bar_empty_left_round.bmp Binary files differdeleted file mode 100644 index c4e2f44fc..000000000 --- a/res/images/progress_bar_empty_left_round.bmp +++ /dev/null diff --git a/res/images/progress_bar_empty_left_round.png b/res/images/progress_bar_empty_left_round.png Binary files differnew file mode 100644 index 000000000..dae7d5d13 --- /dev/null +++ b/res/images/progress_bar_empty_left_round.png diff --git a/res/images/progress_bar_empty_right_round.bmp b/res/images/progress_bar_empty_right_round.bmp Binary files differdeleted file mode 100644 index 1906f6209..000000000 --- a/res/images/progress_bar_empty_right_round.bmp +++ /dev/null diff --git a/res/images/progress_bar_empty_right_round.png b/res/images/progress_bar_empty_right_round.png Binary files differnew file mode 100644 index 000000000..542708823 --- /dev/null +++ b/res/images/progress_bar_empty_right_round.png diff --git a/res/images/progress_bar_fill.bmp b/res/images/progress_bar_fill.bmp Binary files differdeleted file mode 100644 index 8d57d8117..000000000 --- a/res/images/progress_bar_fill.bmp +++ /dev/null diff --git a/res/images/progress_bar_fill.png b/res/images/progress_bar_fill.png Binary files differnew file mode 100644 index 000000000..37c04b4f4 --- /dev/null +++ b/res/images/progress_bar_fill.png diff --git a/res/images/progress_bar_left_round.bmp b/res/images/progress_bar_left_round.bmp Binary files differdeleted file mode 100644 index 6d2df8d6a..000000000 --- a/res/images/progress_bar_left_round.bmp +++ /dev/null diff --git a/res/images/progress_bar_left_round.png b/res/images/progress_bar_left_round.png Binary files differnew file mode 100644 index 000000000..e72af47d4 --- /dev/null +++ b/res/images/progress_bar_left_round.png diff --git a/res/images/progress_bar_right_round.bmp b/res/images/progress_bar_right_round.bmp Binary files differdeleted file mode 100644 index 68bb6fe37..000000000 --- a/res/images/progress_bar_right_round.bmp +++ /dev/null diff --git a/res/images/progress_bar_right_round.png b/res/images/progress_bar_right_round.png Binary files differnew file mode 100644 index 000000000..d04c980b9 --- /dev/null +++ b/res/images/progress_bar_right_round.png @@ -46,7 +46,6 @@ static gr_surface gProgressBarEmpty[NUM_SIDES]; static gr_surface gProgressBarFill[NUM_SIDES]; static const struct { gr_surface* surface; const char *name; } BITMAPS[] = { - { &gBackgroundIcon[BACKGROUND_ICON_UNPACKING], "icon_unpacking" }, { &gBackgroundIcon[BACKGROUND_ICON_INSTALLING], "icon_installing" }, { &gBackgroundIcon[BACKGROUND_ICON_ERROR], "icon_error" }, { &gBackgroundIcon[BACKGROUND_ICON_FIRMWARE_INSTALLING], diff --git a/updater/Android.mk b/updater/Android.mk new file mode 100644 index 000000000..897b9d74c --- /dev/null +++ b/updater/Android.mk @@ -0,0 +1,30 @@ +# Copyright 2009 The Android Open Source Project + +LOCAL_PATH := $(call my-dir) + +updater_src_files := \ + install.c \ + updater.c + +# +# Build a statically-linked binary to include in OTA packages +# +include $(CLEAR_VARS) + +# Build only in eng, so we don't end up with a copy of this in /system +# on user builds. (TODO: find a better way to build device binaries +# needed only for OTA packages.) +LOCAL_MODULE_TAGS := eng + +LOCAL_SRC_FILES := $(updater_src_files) + +LOCAL_STATIC_LIBRARIES := libapplypatch libedify libmtdutils libminzip libz +LOCAL_STATIC_LIBRARIES += libmincrypt libbz +LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc +LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. + +LOCAL_MODULE := updater + +LOCAL_FORCE_STATIC_EXECUTABLE := true + +include $(BUILD_EXECUTABLE) diff --git a/updater/install.c b/updater/install.c new file mode 100644 index 000000000..c4f5e0341 --- /dev/null +++ b/updater/install.c @@ -0,0 +1,788 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ctype.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "cutils/misc.h" +#include "cutils/properties.h" +#include "edify/expr.h" +#include "minzip/DirUtil.h" +#include "mtdutils/mounts.h" +#include "mtdutils/mtdutils.h" +#include "updater.h" + + +// mount(type, location, mount_point) +// +// what: type="MTD" location="<partition>" to mount a yaffs2 filesystem +// type="vfat" location="/dev/block/<whatever>" to mount a device +char* MountFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + if (argc != 3) { + return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc); + } + char* type; + char* location; + char* mount_point; + if (ReadArgs(state, argv, 3, &type, &location, &mount_point) < 0) { + return NULL; + } + + if (strlen(type) == 0) { + ErrorAbort(state, "type argument to %s() can't be empty", name); + goto done; + } + if (strlen(location) == 0) { + ErrorAbort(state, "location argument to %s() can't be empty", name); + goto done; + } + if (strlen(mount_point) == 0) { + ErrorAbort(state, "mount_point argument to %s() can't be empty", name); + goto done; + } + + mkdir(mount_point, 0755); + + if (strcmp(type, "MTD") == 0) { + mtd_scan_partitions(); + const MtdPartition* mtd; + mtd = mtd_find_partition_by_name(location); + if (mtd == NULL) { + fprintf(stderr, "%s: no mtd partition named \"%s\"", + name, location); + result = strdup(""); + goto done; + } + if (mtd_mount_partition(mtd, mount_point, "yaffs2", 0 /* rw */) != 0) { + fprintf(stderr, "mtd mount of %s failed: %s\n", + location, strerror(errno)); + result = strdup(""); + goto done; + } + result = mount_point; + } else { + if (mount(location, mount_point, type, + MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) { + result = strdup(""); + } else { + result = mount_point; + } + } + +done: + free(type); + free(location); + if (result != mount_point) free(mount_point); + return result; +} + + +// is_mounted(mount_point) +char* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + if (argc != 1) { + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + } + char* mount_point; + if (ReadArgs(state, argv, 1, &mount_point) < 0) { + return NULL; + } + if (strlen(mount_point) == 0) { + ErrorAbort(state, "mount_point argument to unmount() can't be empty"); + goto done; + } + + scan_mounted_volumes(); + const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point); + if (vol == NULL) { + result = strdup(""); + } else { + result = mount_point; + } + +done: + if (result != mount_point) free(mount_point); + return result; +} + + +char* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + if (argc != 1) { + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + } + char* mount_point; + if (ReadArgs(state, argv, 1, &mount_point) < 0) { + return NULL; + } + if (strlen(mount_point) == 0) { + ErrorAbort(state, "mount_point argument to unmount() can't be empty"); + goto done; + } + + scan_mounted_volumes(); + const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point); + if (vol == NULL) { + fprintf(stderr, "unmount of %s failed; no such volume\n", mount_point); + result = strdup(""); + } else { + unmount_mounted_volume(vol); + result = mount_point; + } + +done: + if (result != mount_point) free(mount_point); + return result; +} + + +// format(type, location) +// +// type="MTD" location=partition +char* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + char* type; + char* location; + if (ReadArgs(state, argv, 2, &type, &location) < 0) { + return NULL; + } + + if (strlen(type) == 0) { + ErrorAbort(state, "type argument to %s() can't be empty", name); + goto done; + } + if (strlen(location) == 0) { + ErrorAbort(state, "location argument to %s() can't be empty", name); + goto done; + } + + if (strcmp(type, "MTD") == 0) { + mtd_scan_partitions(); + const MtdPartition* mtd = mtd_find_partition_by_name(location); + if (mtd == NULL) { + fprintf(stderr, "%s: no mtd partition named \"%s\"", + name, location); + result = strdup(""); + goto done; + } + MtdWriteContext* ctx = mtd_write_partition(mtd); + if (ctx == NULL) { + fprintf(stderr, "%s: can't write \"%s\"", name, location); + result = strdup(""); + goto done; + } + if (mtd_erase_blocks(ctx, -1) == -1) { + mtd_write_close(ctx); + fprintf(stderr, "%s: failed to erase \"%s\"", name, location); + result = strdup(""); + goto done; + } + if (mtd_write_close(ctx) != 0) { + fprintf(stderr, "%s: failed to close \"%s\"", name, location); + result = strdup(""); + goto done; + } + result = location; + } else { + fprintf(stderr, "%s: unsupported type \"%s\"", name, type); + } + +done: + free(type); + if (result != location) free(location); + return result; +} + + +char* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) { + char** paths = malloc(argc * sizeof(char*)); + int i; + for (i = 0; i < argc; ++i) { + paths[i] = Evaluate(state, argv[i]); + if (paths[i] == NULL) { + int j; + for (j = 0; j < i; ++i) { + free(paths[j]); + } + free(paths); + return NULL; + } + } + + bool recursive = (strcmp(name, "delete_recursive") == 0); + + int success = 0; + for (i = 0; i < argc; ++i) { + if ((recursive ? dirUnlinkHierarchy(paths[i]) : unlink(paths[i])) == 0) + ++success; + free(paths[i]); + } + free(paths); + + char buffer[10]; + sprintf(buffer, "%d", success); + return strdup(buffer); +} + + +char* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + char* frac_str; + char* sec_str; + if (ReadArgs(state, argv, 2, &frac_str, &sec_str) < 0) { + return NULL; + } + + double frac = strtod(frac_str, NULL); + int sec = strtol(sec_str, NULL, 10); + + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); + fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec); + + free(sec_str); + return frac_str; +} + +char* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 1) { + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + } + char* frac_str; + if (ReadArgs(state, argv, 1, &frac_str) < 0) { + return NULL; + } + + double frac = strtod(frac_str, NULL); + + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); + fprintf(ui->cmd_pipe, "set_progress %f\n", frac); + + return frac_str; +} + +// package_extract_dir(package_path, destination_path) +char* PackageExtractDirFn(const char* name, State* state, + int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + char* zip_path; + char* dest_path; + if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; + + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + + // To create a consistent system image, never use the clock for timestamps. + struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default + + bool success = mzExtractRecursive(za, zip_path, dest_path, + MZ_EXTRACT_FILES_ONLY, ×tamp, + NULL, NULL); + free(zip_path); + free(dest_path); + return strdup(success ? "t" : ""); +} + + +// package_extract_file(package_path, destination_path) +char* PackageExtractFileFn(const char* name, State* state, + int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + char* zip_path; + char* dest_path; + if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; + + bool success = false; + + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + const ZipEntry* entry = mzFindZipEntry(za, zip_path); + if (entry == NULL) { + fprintf(stderr, "%s: no %s in package\n", name, zip_path); + goto done; + } + + FILE* f = fopen(dest_path, "wb"); + if (f == NULL) { + fprintf(stderr, "%s: can't open %s for write: %s\n", + name, dest_path, strerror(errno)); + goto done; + } + success = mzExtractZipEntryToFile(za, entry, fileno(f)); + fclose(f); + + done: + free(zip_path); + free(dest_path); + return strdup(success ? "t" : ""); +} + + +// symlink target src1 src2 ... +char* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc == 0) { + return ErrorAbort(state, "%s() expects 1+ args, got %d", name, argc); + } + char* target; + target = Evaluate(state, argv[0]); + if (target == NULL) return NULL; + + char** srcs = ReadVarArgs(state, argc-1, argv+1); + if (srcs == NULL) { + free(target); + return NULL; + } + + int i; + for (i = 0; i < argc-1; ++i) { + symlink(target, srcs[i]); + free(srcs[i]); + } + free(srcs); + return strdup(""); +} + + +char* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + bool recursive = (strcmp(name, "set_perm_recursive") == 0); + + int min_args = 4 + (recursive ? 1 : 0); + if (argc < min_args) { + return ErrorAbort(state, "%s() expects %d+ args, got %d", name, argc); + } + + char** args = ReadVarArgs(state, argc, argv); + if (args == NULL) return NULL; + + char* end; + int i; + + int uid = strtoul(args[0], &end, 0); + if (*end != '\0' || args[0][0] == 0) { + ErrorAbort(state, "%s: \"%s\" not a valid uid", name, args[0]); + goto done; + } + + int gid = strtoul(args[1], &end, 0); + if (*end != '\0' || args[1][0] == 0) { + ErrorAbort(state, "%s: \"%s\" not a valid gid", name, args[1]); + goto done; + } + + if (recursive) { + int dir_mode = strtoul(args[2], &end, 0); + if (*end != '\0' || args[2][0] == 0) { + ErrorAbort(state, "%s: \"%s\" not a valid dirmode", name, args[2]); + goto done; + } + + int file_mode = strtoul(args[3], &end, 0); + if (*end != '\0' || args[3][0] == 0) { + ErrorAbort(state, "%s: \"%s\" not a valid filemode", + name, args[3]); + goto done; + } + + for (i = 4; i < argc; ++i) { + dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode); + } + } else { + int mode = strtoul(args[2], &end, 0); + if (*end != '\0' || args[2][0] == 0) { + ErrorAbort(state, "%s: \"%s\" not a valid mode", name, args[2]); + goto done; + } + + for (i = 3; i < argc; ++i) { + chown(args[i], uid, gid); + chmod(args[i], mode); + } + } + result = strdup(""); + +done: + for (i = 0; i < argc; ++i) { + free(args[i]); + } + free(args); + + return result; +} + + +char* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 1) { + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + } + char* key; + key = Evaluate(state, argv[0]); + if (key == NULL) return NULL; + + char value[PROPERTY_VALUE_MAX]; + property_get(key, value, ""); + free(key); + + return strdup(value); +} + + +// file_getprop(file, key) +// +// interprets 'file' as a getprop-style file (key=value pairs, one +// per line, # comment lines and blank lines okay), and returns the value +// for 'key' (or "" if it isn't defined). +char* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + char* buffer = NULL; + char* filename; + char* key; + if (ReadArgs(state, argv, 2, &filename, &key) < 0) { + return NULL; + } + + struct stat st; + if (stat(filename, &st) < 0) { + ErrorAbort(state, "%s: failed to stat \"%s\": %s", + name, filename, strerror(errno)); + goto done; + } + +#define MAX_FILE_GETPROP_SIZE 65536 + + if (st.st_size > MAX_FILE_GETPROP_SIZE) { + ErrorAbort(state, "%s too large for %s (max %d)", + filename, name, MAX_FILE_GETPROP_SIZE); + goto done; + } + + buffer = malloc(st.st_size+1); + if (buffer == NULL) { + ErrorAbort(state, "%s: failed to alloc %d bytes", name, st.st_size+1); + goto done; + } + + FILE* f = fopen(filename, "rb"); + if (f == NULL) { + ErrorAbort(state, "%s: failed to open %s: %s", + name, filename, strerror(errno)); + goto done; + } + + if (fread(buffer, 1, st.st_size, f) != st.st_size) { + ErrorAbort(state, "%s: failed to read %d bytes from %s", + name, st.st_size+1, filename); + fclose(f); + goto done; + } + buffer[st.st_size] = '\0'; + + fclose(f); + + char* line = strtok(buffer, "\n"); + do { + // skip whitespace at start of line + while (*line && isspace(*line)) ++line; + + // comment or blank line: skip to next line + if (*line == '\0' || *line == '#') continue; + + char* equal = strchr(line, '='); + if (equal == NULL) { + ErrorAbort(state, "%s: malformed line \"%s\": %s not a prop file?", + name, line, filename); + goto done; + } + + // trim whitespace between key and '=' + char* key_end = equal-1; + while (key_end > line && isspace(*key_end)) --key_end; + key_end[1] = '\0'; + + // not the key we're looking for + if (strcmp(key, line) != 0) continue; + + // skip whitespace after the '=' to the start of the value + char* val_start = equal+1; + while(*val_start && isspace(*val_start)) ++val_start; + + // trim trailing whitespace + char* val_end = val_start + strlen(val_start)-1; + while (val_end > val_start && isspace(*val_end)) --val_end; + val_end[1] = '\0'; + + result = strdup(val_start); + break; + + } while ((line = strtok(NULL, "\n"))); + + if (result == NULL) result = strdup(""); + + done: + free(filename); + free(key); + free(buffer); + return result; +} + + +static bool write_raw_image_cb(const unsigned char* data, + int data_len, void* ctx) { + int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len); + if (r == data_len) return true; + fprintf(stderr, "%s\n", strerror(errno)); + return false; +} + +// write_raw_image(file, partition) +char* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + + char* partition; + char* filename; + if (ReadArgs(state, argv, 2, &filename, &partition) < 0) { + return NULL; + } + + if (strlen(partition) == 0) { + ErrorAbort(state, "partition argument to %s can't be empty", name); + goto done; + } + if (strlen(filename) == 0) { + ErrorAbort(state, "file argument to %s can't be empty", name); + goto done; + } + + mtd_scan_partitions(); + const MtdPartition* mtd = mtd_find_partition_by_name(partition); + if (mtd == NULL) { + fprintf(stderr, "%s: no mtd partition named \"%s\"\n", name, partition); + result = strdup(""); + goto done; + } + + MtdWriteContext* ctx = mtd_write_partition(mtd); + if (ctx == NULL) { + fprintf(stderr, "%s: can't write mtd partition \"%s\"\n", + name, partition); + result = strdup(""); + goto done; + } + + bool success; + + FILE* f = fopen(filename, "rb"); + if (f == NULL) { + fprintf(stderr, "%s: can't open %s: %s\n", + name, filename, strerror(errno)); + result = strdup(""); + goto done; + } + + success = true; + char* buffer = malloc(BUFSIZ); + int read; + while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) { + int wrote = mtd_write_data(ctx, buffer, read); + success = success && (wrote == read); + if (!success) { + fprintf(stderr, "mtd_write_data to %s failed: %s\n", + partition, strerror(errno)); + } + } + free(buffer); + fclose(f); + + if (mtd_erase_blocks(ctx, -1) == -1) { + fprintf(stderr, "%s: error erasing blocks of %s\n", name, partition); + } + if (mtd_write_close(ctx) != 0) { + fprintf(stderr, "%s: error closing write of %s\n", name, partition); + } + + printf("%s %s partition from %s\n", + success ? "wrote" : "failed to write", partition, filename); + + result = success ? partition : strdup(""); + +done: + if (result != partition) free(partition); + free(filename); + return result; +} + +// write_firmware_image(file, partition) +// +// partition is "radio" or "hboot" +// file is not used until after updater exits +// +// TODO: this should live in some HTC-specific library +char* WriteFirmwareImageFn(const char* name, State* state, + int argc, Expr* argv[]) { + char* result = NULL; + + char* partition; + char* filename; + if (ReadArgs(state, argv, 2, &filename, &partition) < 0) { + return NULL; + } + + if (strlen(partition) == 0) { + ErrorAbort(state, "partition argument to %s can't be empty", name); + goto done; + } + if (strlen(filename) == 0) { + ErrorAbort(state, "file argument to %s can't be empty", name); + goto done; + } + + FILE* cmd = ((UpdaterInfo*)(state->cookie))->cmd_pipe; + fprintf(cmd, "firmware %s %s\n", partition, filename); + + printf("will write %s firmware from %s\n", partition, filename); + result = partition; + +done: + if (result != partition) free(partition); + free(filename); + return result; +} + + +extern int applypatch(int argc, char** argv); + +// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1:patch, ...) +// apply_patch_check(file, sha1, ...) +// apply_patch_space(bytes) +char* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { + printf("in applypatchfn (%s)\n", name); + + char* prepend = NULL; + if (strstr(name, "check") != NULL) { + prepend = "-c"; + } else if (strstr(name, "space") != NULL) { + prepend = "-s"; + } + + char** args = ReadVarArgs(state, argc, argv); + if (args == NULL) return NULL; + + // insert the "program name" argv[0] and a copy of the "prepend" + // string (if any) at the start of the args. + + int extra = 1 + (prepend != NULL ? 1 : 0); + char** temp = malloc((argc+extra) * sizeof(char*)); + memcpy(temp+extra, args, argc * sizeof(char*)); + temp[0] = strdup("updater"); + if (prepend) { + temp[1] = strdup(prepend); + } + free(args); + args = temp; + argc += extra; + + printf("calling applypatch\n"); + fflush(stdout); + int result = applypatch(argc, args); + printf("applypatch returned %d\n", result); + + int i; + for (i = 0; i < argc; ++i) { + free(args[i]); + } + free(args); + + switch (result) { + case 0: return strdup("t"); + case 1: return strdup(""); + default: return ErrorAbort(state, "applypatch couldn't parse args"); + } +} + +char* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) { + char** args = ReadVarArgs(state, argc, argv); + if (args == NULL) { + return NULL; + } + + int size = 0; + int i; + for (i = 0; i < argc; ++i) { + size += strlen(args[i]); + } + char* buffer = malloc(size+1); + size = 0; + for (i = 0; i < argc; ++i) { + strcpy(buffer+size, args[i]); + size += strlen(args[i]); + free(args[i]); + } + free(args); + buffer[size] = '\0'; + + char* line = strtok(buffer, "\n"); + while (line) { + fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, + "ui_print %s\n", line); + line = strtok(NULL, "\n"); + } + fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "ui_print\n"); + + return buffer; +} + + +void RegisterInstallFunctions() { + RegisterFunction("mount", MountFn); + RegisterFunction("is_mounted", IsMountedFn); + RegisterFunction("unmount", UnmountFn); + RegisterFunction("format", FormatFn); + RegisterFunction("show_progress", ShowProgressFn); + RegisterFunction("set_progress", SetProgressFn); + RegisterFunction("delete", DeleteFn); + RegisterFunction("delete_recursive", DeleteFn); + RegisterFunction("package_extract_dir", PackageExtractDirFn); + RegisterFunction("package_extract_file", PackageExtractFileFn); + RegisterFunction("symlink", SymlinkFn); + RegisterFunction("set_perm", SetPermFn); + RegisterFunction("set_perm_recursive", SetPermFn); + + RegisterFunction("getprop", GetPropFn); + RegisterFunction("file_getprop", FileGetPropFn); + RegisterFunction("write_raw_image", WriteRawImageFn); + RegisterFunction("write_firmware_image", WriteFirmwareImageFn); + + RegisterFunction("apply_patch", ApplyPatchFn); + RegisterFunction("apply_patch_check", ApplyPatchFn); + RegisterFunction("apply_patch_space", ApplyPatchFn); + + RegisterFunction("ui_print", UIPrintFn); +} diff --git a/updater/install.h b/updater/install.h new file mode 100644 index 000000000..94f344f8e --- /dev/null +++ b/updater/install.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UPDATER_INSTALL_H_ +#define _UPDATER_INSTALL_H_ + +void RegisterInstallFunctions(); + +#endif diff --git a/updater/updater.c b/updater/updater.c new file mode 100644 index 000000000..31d93ae96 --- /dev/null +++ b/updater/updater.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> + +#include "edify/expr.h" +#include "updater.h" +#include "install.h" +#include "minzip/Zip.h" + +// Where in the package we expect to find the edify script to execute. +// (Note it's "updateR-script", not the older "update-script".) +#define SCRIPT_NAME "META-INF/com/google/android/updater-script" + +int main(int argc, char** argv) { + if (argc != 4) { + fprintf(stderr, "unexpected number of arguments (%d)\n", argc); + return 1; + } + + char* version = argv[1]; + if ((version[0] != '1' && version[0] != '2') || version[1] != '\0') { + // We support version "1" or "2". + fprintf(stderr, "wrong updater binary API; expected 1 or 2, got %s\n", + argv[1]); + return 2; + } + + // Set up the pipe for sending commands back to the parent process. + + int fd = atoi(argv[2]); + FILE* cmd_pipe = fdopen(fd, "wb"); + setlinebuf(cmd_pipe); + + // Extract the script from the package. + + char* package_data = argv[3]; + ZipArchive za; + int err; + err = mzOpenZipArchive(package_data, &za); + if (err != 0) { + fprintf(stderr, "failed to open package %s: %s\n", + package_data, strerror(err)); + return 3; + } + + const ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME); + if (script_entry == NULL) { + fprintf(stderr, "failed to find %s in %s\n", SCRIPT_NAME, package_data); + return 4; + } + + char* script = malloc(script_entry->uncompLen+1); + if (!mzReadZipEntry(&za, script_entry, script, script_entry->uncompLen)) { + fprintf(stderr, "failed to read script from package\n"); + return 5; + } + script[script_entry->uncompLen] = '\0'; + + // Configure edify's functions. + + RegisterBuiltins(); + RegisterInstallFunctions(); + FinishRegistration(); + + // Parse the script. + + Expr* root; + int error_count = 0; + yy_scan_string(script); + int error = yyparse(&root, &error_count); + if (error != 0 || error_count > 0) { + fprintf(stderr, "%d parse errors\n", error_count); + return 6; + } + + // Evaluate the parsed script. + + UpdaterInfo updater_info; + updater_info.cmd_pipe = cmd_pipe; + updater_info.package_zip = &za; + + State state; + state.cookie = &updater_info; + state.script = script; + state.errmsg = NULL; + + char* result = Evaluate(&state, root); + if (result == NULL) { + if (state.errmsg == NULL) { + fprintf(stderr, "script aborted (no error message)\n"); + fprintf(cmd_pipe, "ui_print script aborted (no error message)\n"); + } else { + fprintf(stderr, "script aborted: %s\n", state.errmsg); + char* line = strtok(state.errmsg, "\n"); + while (line) { + fprintf(cmd_pipe, "ui_print %s\n", line); + line = strtok(NULL, "\n"); + } + fprintf(cmd_pipe, "ui_print\n"); + } + free(state.errmsg); + return 7; + } else { + fprintf(stderr, "script result was [%s]\n", result); + free(result); + } + + mzCloseZipArchive(&za); + free(script); + + return 0; +} diff --git a/updater/updater.h b/updater/updater.h new file mode 100644 index 000000000..22fbfd285 --- /dev/null +++ b/updater/updater.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UPDATER_UPDATER_H_ +#define _UPDATER_UPDATER_H_ + +#include <stdio.h> +#include "minzip/Zip.h" + +typedef struct { + FILE* cmd_pipe; + ZipArchive* package_zip; +} UpdaterInfo; + +#endif |