summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean-Baptiste Queru <jbq@google.com>2009-07-26 02:48:00 +0200
committerJean-Baptiste Queru <jbq@google.com>2009-07-26 02:48:00 +0200
commit7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7 (patch)
tree88c0fc6a69cade6f9d4437666c6420e0f757e278
parentMerge commit 'korg/cupcake' (diff)
parentskip over all-zero blocks when reading MTD partition (diff)
downloadandroid_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
-rw-r--r--Android.mk28
-rw-r--r--amend/Android.mk4
-rw-r--r--amend/amend.c1
-rw-r--r--amend/commands.c88
-rw-r--r--amend/commands.h32
-rw-r--r--amend/main.c6
-rw-r--r--amend/permissions.c270
-rw-r--r--amend/permissions.h111
-rw-r--r--amend/register.c88
-rw-r--r--amend/test_commands.c77
-rw-r--r--amend/test_permissions.c347
-rw-r--r--commands.c117
-rw-r--r--common.h4
-rw-r--r--edify/Android.mk39
-rw-r--r--edify/README108
-rw-r--r--edify/expr.c432
-rw-r--r--edify/expr.h126
-rw-r--r--edify/lexer.l110
-rw-r--r--edify/main.c213
-rw-r--r--edify/parser.y130
-rw-r--r--edify/yydefs.h36
-rw-r--r--firmware.c4
-rw-r--r--firmware.h3
-rw-r--r--install.c313
-rw-r--r--minui/resources.c167
-rw-r--r--minzip/Zip.c38
-rw-r--r--mtdutils/mtdutils.c13
-rw-r--r--recovery.c23
-rw-r--r--res/images/icon_error.bmpbin91076 -> 0 bytes
-rw-r--r--res/images/icon_error.pngbin0 -> 9616 bytes
-rw-r--r--res/images/icon_firmware_error.bmpbin91076 -> 0 bytes
-rw-r--r--res/images/icon_firmware_error.pngbin0 -> 8088 bytes
-rw-r--r--res/images/icon_firmware_install.bmpbin91076 -> 0 bytes
-rw-r--r--res/images/icon_firmware_install.pngbin0 -> 11986 bytes
-rw-r--r--res/images/icon_installing.bmpbin91076 -> 0 bytes
-rw-r--r--res/images/icon_installing.pngbin0 -> 10138 bytes
-rw-r--r--res/images/icon_unpacking.bmpbin91076 -> 0 bytes
-rw-r--r--res/images/indeterminate1.bmpbin20214 -> 0 bytes
-rw-r--r--res/images/indeterminate1.pngbin0 -> 2249 bytes
-rw-r--r--res/images/indeterminate2.bmpbin20214 -> 0 bytes
-rw-r--r--res/images/indeterminate2.pngbin0 -> 2251 bytes
-rw-r--r--res/images/indeterminate3.bmpbin20214 -> 0 bytes
-rw-r--r--res/images/indeterminate3.pngbin0 -> 2254 bytes
-rw-r--r--res/images/indeterminate4.bmpbin20214 -> 0 bytes
-rw-r--r--res/images/indeterminate4.pngbin0 -> 2249 bytes
-rw-r--r--res/images/indeterminate5.bmpbin20214 -> 0 bytes
-rw-r--r--res/images/indeterminate5.pngbin0 -> 2246 bytes
-rw-r--r--res/images/indeterminate6.bmpbin20214 -> 0 bytes
-rw-r--r--res/images/indeterminate6.pngbin0 -> 2262 bytes
-rw-r--r--res/images/progress_bar_empty.bmpbin136 -> 0 bytes
-rw-r--r--res/images/progress_bar_empty.pngbin0 -> 148 bytes
-rw-r--r--res/images/progress_bar_empty_left_round.bmpbin294 -> 0 bytes
-rw-r--r--res/images/progress_bar_empty_left_round.pngbin0 -> 220 bytes
-rw-r--r--res/images/progress_bar_empty_right_round.bmpbin294 -> 0 bytes
-rw-r--r--res/images/progress_bar_empty_right_round.pngbin0 -> 211 bytes
-rw-r--r--res/images/progress_bar_fill.bmpbin136 -> 0 bytes
-rw-r--r--res/images/progress_bar_fill.pngbin0 -> 117 bytes
-rw-r--r--res/images/progress_bar_left_round.bmpbin294 -> 0 bytes
-rw-r--r--res/images/progress_bar_left_round.pngbin0 -> 195 bytes
-rw-r--r--res/images/progress_bar_right_round.bmpbin294 -> 0 bytes
-rw-r--r--res/images/progress_bar_right_round.pngbin0 -> 192 bytes
-rw-r--r--ui.c1
-rw-r--r--updater/Android.mk30
-rw-r--r--updater/install.c788
-rw-r--r--updater/install.h22
-rw-r--r--updater/updater.c128
-rw-r--r--updater/updater.h28
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);
diff --git a/common.h b/common.h
index e17f76a46..67611592a 100644
--- a/common.h
+++ b/common.h
@@ -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...
diff --git a/install.c b/install.c
index 069112080..ab19478cf 100644
--- a/install.c
+++ b/install.c
@@ -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
deleted file mode 100644
index 7eb2bbc77..000000000
--- a/res/images/icon_error.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/icon_error.png b/res/images/icon_error.png
new file mode 100644
index 000000000..7064c2e23
--- /dev/null
+++ b/res/images/icon_error.png
Binary files differ
diff --git a/res/images/icon_firmware_error.bmp b/res/images/icon_firmware_error.bmp
deleted file mode 100644
index 5b8649f17..000000000
--- a/res/images/icon_firmware_error.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/icon_firmware_error.png b/res/images/icon_firmware_error.png
new file mode 100644
index 000000000..0c32c9ede
--- /dev/null
+++ b/res/images/icon_firmware_error.png
Binary files differ
diff --git a/res/images/icon_firmware_install.bmp b/res/images/icon_firmware_install.bmp
deleted file mode 100644
index b0f5f959b..000000000
--- a/res/images/icon_firmware_install.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/icon_firmware_install.png b/res/images/icon_firmware_install.png
new file mode 100644
index 000000000..ee2afac5d
--- /dev/null
+++ b/res/images/icon_firmware_install.png
Binary files differ
diff --git a/res/images/icon_installing.bmp b/res/images/icon_installing.bmp
deleted file mode 100644
index fff99fd7e..000000000
--- a/res/images/icon_installing.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/icon_installing.png b/res/images/icon_installing.png
new file mode 100644
index 000000000..f24f2e33f
--- /dev/null
+++ b/res/images/icon_installing.png
Binary files differ
diff --git a/res/images/icon_unpacking.bmp b/res/images/icon_unpacking.bmp
deleted file mode 100644
index ab6548c58..000000000
--- a/res/images/icon_unpacking.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate1.bmp b/res/images/indeterminate1.bmp
deleted file mode 100644
index 716c92568..000000000
--- a/res/images/indeterminate1.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate1.png b/res/images/indeterminate1.png
new file mode 100644
index 000000000..264bf27e5
--- /dev/null
+++ b/res/images/indeterminate1.png
Binary files differ
diff --git a/res/images/indeterminate2.bmp b/res/images/indeterminate2.bmp
deleted file mode 100644
index 223cd3c1e..000000000
--- a/res/images/indeterminate2.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate2.png b/res/images/indeterminate2.png
new file mode 100644
index 000000000..c30c049ab
--- /dev/null
+++ b/res/images/indeterminate2.png
Binary files differ
diff --git a/res/images/indeterminate3.bmp b/res/images/indeterminate3.bmp
deleted file mode 100644
index fd9086a1f..000000000
--- a/res/images/indeterminate3.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate3.png b/res/images/indeterminate3.png
new file mode 100644
index 000000000..891a00095
--- /dev/null
+++ b/res/images/indeterminate3.png
Binary files differ
diff --git a/res/images/indeterminate4.bmp b/res/images/indeterminate4.bmp
deleted file mode 100644
index 87b264034..000000000
--- a/res/images/indeterminate4.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate4.png b/res/images/indeterminate4.png
new file mode 100644
index 000000000..7a6415149
--- /dev/null
+++ b/res/images/indeterminate4.png
Binary files differ
diff --git a/res/images/indeterminate5.bmp b/res/images/indeterminate5.bmp
deleted file mode 100644
index e16efb04c..000000000
--- a/res/images/indeterminate5.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate5.png b/res/images/indeterminate5.png
new file mode 100644
index 000000000..cd6ab20a7
--- /dev/null
+++ b/res/images/indeterminate5.png
Binary files differ
diff --git a/res/images/indeterminate6.bmp b/res/images/indeterminate6.bmp
deleted file mode 100644
index 085ad951a..000000000
--- a/res/images/indeterminate6.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate6.png b/res/images/indeterminate6.png
new file mode 100644
index 000000000..ddd9e7384
--- /dev/null
+++ b/res/images/indeterminate6.png
Binary files differ
diff --git a/res/images/progress_bar_empty.bmp b/res/images/progress_bar_empty.bmp
deleted file mode 100644
index 8e512fd92..000000000
--- a/res/images/progress_bar_empty.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/progress_bar_empty.png b/res/images/progress_bar_empty.png
new file mode 100644
index 000000000..9013f04ac
--- /dev/null
+++ b/res/images/progress_bar_empty.png
Binary files differ
diff --git a/res/images/progress_bar_empty_left_round.bmp b/res/images/progress_bar_empty_left_round.bmp
deleted file mode 100644
index c4e2f44fc..000000000
--- a/res/images/progress_bar_empty_left_round.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/progress_bar_empty_left_round.png b/res/images/progress_bar_empty_left_round.png
new file mode 100644
index 000000000..dae7d5d13
--- /dev/null
+++ b/res/images/progress_bar_empty_left_round.png
Binary files differ
diff --git a/res/images/progress_bar_empty_right_round.bmp b/res/images/progress_bar_empty_right_round.bmp
deleted file mode 100644
index 1906f6209..000000000
--- a/res/images/progress_bar_empty_right_round.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/progress_bar_empty_right_round.png b/res/images/progress_bar_empty_right_round.png
new file mode 100644
index 000000000..542708823
--- /dev/null
+++ b/res/images/progress_bar_empty_right_round.png
Binary files differ
diff --git a/res/images/progress_bar_fill.bmp b/res/images/progress_bar_fill.bmp
deleted file mode 100644
index 8d57d8117..000000000
--- a/res/images/progress_bar_fill.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/progress_bar_fill.png b/res/images/progress_bar_fill.png
new file mode 100644
index 000000000..37c04b4f4
--- /dev/null
+++ b/res/images/progress_bar_fill.png
Binary files differ
diff --git a/res/images/progress_bar_left_round.bmp b/res/images/progress_bar_left_round.bmp
deleted file mode 100644
index 6d2df8d6a..000000000
--- a/res/images/progress_bar_left_round.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/progress_bar_left_round.png b/res/images/progress_bar_left_round.png
new file mode 100644
index 000000000..e72af47d4
--- /dev/null
+++ b/res/images/progress_bar_left_round.png
Binary files differ
diff --git a/res/images/progress_bar_right_round.bmp b/res/images/progress_bar_right_round.bmp
deleted file mode 100644
index 68bb6fe37..000000000
--- a/res/images/progress_bar_right_round.bmp
+++ /dev/null
Binary files differ
diff --git a/res/images/progress_bar_right_round.png b/res/images/progress_bar_right_round.png
new file mode 100644
index 000000000..d04c980b9
--- /dev/null
+++ b/res/images/progress_bar_right_round.png
Binary files differ
diff --git a/ui.c b/ui.c
index 5d06561c5..b84f1722f 100644
--- a/ui.c
+++ b/ui.c
@@ -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, &timestamp,
+ NULL, NULL);
+ free(zip_path);
+ free(dest_path);
+ return strdup(success ? "t" : "");
+}
+
+
+// package_extract_file(package_path, destination_path)
+char* PackageExtractFileFn(const char* name, State* state,
+ int argc, Expr* argv[]) {
+ if (argc != 2) {
+ return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
+ }
+ char* zip_path;
+ char* dest_path;
+ if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
+
+ bool success = false;
+
+ ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
+ const ZipEntry* entry = mzFindZipEntry(za, zip_path);
+ if (entry == NULL) {
+ fprintf(stderr, "%s: no %s in package\n", name, zip_path);
+ goto done;
+ }
+
+ FILE* f = fopen(dest_path, "wb");
+ if (f == NULL) {
+ fprintf(stderr, "%s: can't open %s for write: %s\n",
+ name, dest_path, strerror(errno));
+ goto done;
+ }
+ success = mzExtractZipEntryToFile(za, entry, fileno(f));
+ fclose(f);
+
+ done:
+ free(zip_path);
+ free(dest_path);
+ return strdup(success ? "t" : "");
+}
+
+
+// symlink target src1 src2 ...
+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