Merge korg/donut into korg/master

This commit is contained in:
Jean-Baptiste Queru 2009-07-25 17:48:00 -07:00
commit 7bd5c66075
67 changed files with 2704 additions and 1221 deletions

View File

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

View File

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

View File

@ -17,6 +17,7 @@
#include <stdlib.h>
#include "amend.h"
#include "lexer.h"
#include "parser.h"
extern const AmCommandList *gCommands;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

39
edify/Android.mk Normal file
View File

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

108
edify/README Normal file
View File

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

432
edify/expr.c Normal file
View File

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

126
edify/expr.h Normal file
View File

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

110
edify/lexer.l Normal file
View File

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

213
edify/main.c Normal file
View File

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

130
edify/parser.y Normal file
View File

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

36
edify/yydefs.h Normal file
View File

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

View File

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

View File

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

313
install.c
View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

BIN
res/images/icon_error.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

1
ui.c
View File

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

30
updater/Android.mk Normal file
View File

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

788
updater/install.c Normal file
View File

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

22
updater/install.h Normal file
View File

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

128
updater/updater.c Normal file
View File

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

28
updater/updater.h Normal file
View File

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