auto import from //depot/cupcake/@135843

This commit is contained in:
The Android Open Source Project 2009-03-03 19:28:42 -08:00
parent ffb48f64fe
commit c24a8e688a
135 changed files with 14368 additions and 0 deletions

60
Android.mk Normal file
View file

@ -0,0 +1,60 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
commands_recovery_local_path := $(LOCAL_PATH)
ifneq ($(TARGET_SIMULATOR),true)
ifeq ($(TARGET_ARCH),arm)
LOCAL_SRC_FILES := \
recovery.c \
bootloader.c \
commands.c \
firmware.c \
install.c \
roots.c \
ui.c \
verifier.c
LOCAL_SRC_FILES += test_roots.c
LOCAL_MODULE := recovery
LOCAL_FORCE_STATIC_EXECUTABLE := true
# 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.
# TODO: Build the ramdisk image in a more principled way.
LOCAL_MODULE_TAGS := eng
LOCAL_STATIC_LIBRARIES := libminzip libunz libamend libmtdutils libmincrypt
LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static 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
commands_recovery_local_path :=

53
amend/Android.mk Normal file
View file

@ -0,0 +1,53 @@
# Copyright 2007 The Android Open Source Project
#
LOCAL_PATH := $(call my-dir)
amend_src_files := \
amend.c \
lexer.l \
parser_y.y \
ast.c \
symtab.c \
commands.c \
permissions.c \
execute.c
amend_test_files := \
test_symtab.c \
test_commands.c \
test_permissions.c
# "-x c" forces the lex/yacc files to be compiled as c;
# the build system otherwise forces them to be c++.
amend_cflags := -Wall -x c
#
# Build the host-side command line tool
#
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
$(amend_src_files) \
$(amend_test_files) \
register.c \
main.c
LOCAL_CFLAGS := $(amend_cflags) -g -O0
LOCAL_MODULE := amend
LOCAL_YACCFLAGS := -v
include $(BUILD_HOST_EXECUTABLE)
#
# Build the device-side library
#
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(amend_src_files)
LOCAL_SRC_FILES += $(amend_test_files)
LOCAL_CFLAGS := $(amend_cflags)
LOCAL_MODULE := libamend
include $(BUILD_STATIC_LIBRARY)

32
amend/amend.c Normal file
View file

@ -0,0 +1,32 @@
/*
* 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 "amend.h"
#include "lexer.h"
extern const AmCommandList *gCommands;
const AmCommandList *
parseAmendScript(const char *buf, size_t bufLen)
{
setLexerInputBuffer(buf, bufLen);
int ret = yyparse();
if (ret != 0) {
return NULL;
}
return gCommands;
}

25
amend/amend.h Normal file
View file

@ -0,0 +1,25 @@
/*
* 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_H_
#define AMEND_H_
#include "ast.h"
#include "execute.h"
const AmCommandList *parseAmendScript(const char *buf, size_t bufLen);
#endif // AMEND_H_

198
amend/ast.c Normal file
View file

@ -0,0 +1,198 @@
/*
* 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 <stdio.h>
#include "ast.h"
static const char gSpaces[] =
" "
" "
" "
" "
" "
" "
" ";
const int gSpacesMax = sizeof(gSpaces) - 1;
static const char *
pad(int level)
{
level *= 4;
if (level > gSpacesMax) {
level = gSpacesMax;
}
return gSpaces + gSpacesMax - level;
}
void dumpBooleanValue(int level, const AmBooleanValue *booleanValue);
void dumpStringValue(int level, const AmStringValue *stringValue);
void
dumpBooleanExpression(int level, const AmBooleanExpression *booleanExpression)
{
const char *op;
bool unary = false;
switch (booleanExpression->op) {
case AM_BOP_NOT:
op = "NOT";
unary = true;
break;
case AM_BOP_EQ:
op = "EQ";
break;
case AM_BOP_NE:
op = "NE";
break;
case AM_BOP_AND:
op = "AND";
break;
case AM_BOP_OR:
op = "OR";
break;
default:
op = "??";
break;
}
printf("%sBOOLEAN %s {\n", pad(level), op);
dumpBooleanValue(level + 1, booleanExpression->arg1);
if (!unary) {
dumpBooleanValue(level + 1, booleanExpression->arg2);
}
printf("%s}\n", pad(level));
}
void
dumpFunctionArguments(int level, const AmFunctionArguments *functionArguments)
{
int i;
for (i = 0; i < functionArguments->argc; i++) {
dumpStringValue(level, &functionArguments->argv[i]);
}
}
void
dumpFunctionCall(int level, const AmFunctionCall *functionCall)
{
printf("%sFUNCTION %s (\n", pad(level), functionCall->name);
dumpFunctionArguments(level + 1, functionCall->args);
printf("%s)\n", pad(level));
}
void
dumpStringValue(int level, const AmStringValue *stringValue)
{
switch (stringValue->type) {
case AM_SVAL_LITERAL:
printf("%s\"%s\"\n", pad(level), stringValue->u.literal);
break;
case AM_SVAL_FUNCTION:
dumpFunctionCall(level, stringValue->u.function);
break;
default:
printf("%s<UNKNOWN SVAL TYPE %d>\n", pad(level), stringValue->type);
break;
}
}
void
dumpStringComparisonExpression(int level,
const AmStringComparisonExpression *stringComparisonExpression)
{
const char *op;
switch (stringComparisonExpression->op) {
case AM_SOP_LT:
op = "LT";
break;
case AM_SOP_LE:
op = "LE";
break;
case AM_SOP_GT:
op = "GT";
break;
case AM_SOP_GE:
op = "GE";
break;
case AM_SOP_EQ:
op = "EQ";
break;
case AM_SOP_NE:
op = "NE";
break;
default:
op = "??";
break;
}
printf("%sSTRING %s {\n", pad(level), op);
dumpStringValue(level + 1, stringComparisonExpression->arg1);
dumpStringValue(level + 1, stringComparisonExpression->arg2);
printf("%s}\n", pad(level));
}
void
dumpBooleanValue(int level, const AmBooleanValue *booleanValue)
{
switch (booleanValue->type) {
case AM_BVAL_EXPRESSION:
dumpBooleanExpression(level, &booleanValue->u.expression);
break;
case AM_BVAL_STRING_COMPARISON:
dumpStringComparisonExpression(level,
&booleanValue->u.stringComparison);
break;
default:
printf("%s<UNKNOWN BVAL TYPE %d>\n", pad(1), booleanValue->type);
break;
}
}
void
dumpWordList(const AmWordList *wordList)
{
int i;
for (i = 0; i < wordList->argc; i++) {
printf("%s\"%s\"\n", pad(1), wordList->argv[i]);
}
}
void
dumpCommandArguments(const AmCommandArguments *commandArguments)
{
if (commandArguments->booleanArgs) {
dumpBooleanValue(1, commandArguments->u.b);
} else {
dumpWordList(commandArguments->u.w);
}
}
void
dumpCommand(const AmCommand *command)
{
printf("command \"%s\" {\n", command->name);
dumpCommandArguments(command->args);
printf("}\n");
}
void
dumpCommandList(const AmCommandList *commandList)
{
int i;
for (i = 0; i < commandList->commandCount; i++) {
dumpCommand(commandList->commands[i]);
}
}

165
amend/ast.h Normal file
View file

@ -0,0 +1,165 @@
/*
* 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_AST_H_
#define AMEND_AST_H_
#include "commands.h"
typedef struct AmStringValue AmStringValue;
typedef struct {
int argc;
AmStringValue *argv;
} AmFunctionArguments;
/* An internal structure used only by the parser;
* will not appear in the output AST.
xxx try to move this into parser.h
*/
typedef struct AmFunctionArgumentBuilder AmFunctionArgumentBuilder;
struct AmFunctionArgumentBuilder {
AmFunctionArgumentBuilder *next;
AmStringValue *arg;
int argCount;
};
typedef struct AmWordListBuilder AmWordListBuilder;
struct AmWordListBuilder {
AmWordListBuilder *next;
const char *word;
int wordCount;
};
typedef struct {
const char *name;
Function *fn;
AmFunctionArguments *args;
} AmFunctionCall;
/* <string-value> ::=
* <literal-string> |
* <function-call>
*/
struct AmStringValue {
unsigned int line;
enum {
AM_SVAL_LITERAL,
AM_SVAL_FUNCTION,
} type;
union {
const char *literal;
//xxx inline instead of using pointers
AmFunctionCall *function;
} u;
};
/* <string-comparison-expression> ::=
* <string-value> <string-comparison-operator> <string-value>
*/
typedef struct {
unsigned int line;
enum {
AM_SOP_LT,
AM_SOP_LE,
AM_SOP_GT,
AM_SOP_GE,
AM_SOP_EQ,
AM_SOP_NE,
} op;
AmStringValue *arg1;
AmStringValue *arg2;
} AmStringComparisonExpression;
/* <boolean-expression> ::=
* ! <boolean-value> |
* <boolean-value> <binary-boolean-operator> <boolean-value>
*/
typedef struct AmBooleanValue AmBooleanValue;
typedef struct {
unsigned int line;
enum {
AM_BOP_NOT,
AM_BOP_EQ,
AM_BOP_NE,
AM_BOP_AND,
AM_BOP_OR,
} op;
AmBooleanValue *arg1;
AmBooleanValue *arg2;
} AmBooleanExpression;
/* <boolean-value> ::=
* <boolean-expression> |
* <string-comparison-expression>
*/
struct AmBooleanValue {
unsigned int line;
enum {
AM_BVAL_EXPRESSION,
AM_BVAL_STRING_COMPARISON,
} type;
union {
AmBooleanExpression expression;
AmStringComparisonExpression stringComparison;
} u;
};
typedef struct {
unsigned int line;
int argc;
const char **argv;
} AmWordList;
typedef struct {
bool booleanArgs;
union {
AmWordList *w;
AmBooleanValue *b;
} u;
} AmCommandArguments;
typedef struct {
unsigned int line;
const char *name;
Command *cmd;
AmCommandArguments *args;
} AmCommand;
typedef struct {
AmCommand **commands;
int commandCount;
int arraySize;
} AmCommandList;
void dumpCommandList(const AmCommandList *commandList);
#endif // AMEND_AST_H_

273
amend/commands.c Normal file
View file

@ -0,0 +1,273 @@
/*
* 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 <stdio.h>
#include "symtab.h"
#include "commands.h"
#if 1
#define TRACE(...) printf(__VA_ARGS__)
#else
#define TRACE(...) /**/
#endif
typedef enum {
CMD_TYPE_UNKNOWN = -1,
CMD_TYPE_COMMAND = 0,
CMD_TYPE_FUNCTION
} CommandType;
typedef struct {
const char *name;
void *cookie;
CommandType type;
CommandArgumentType argType;
CommandHook hook;
} CommandEntry;
static struct {
SymbolTable *symbolTable;
bool commandStateInitialized;
} gCommandState;
int
commandInit()
{
if (gCommandState.commandStateInitialized) {
return -1;
}
gCommandState.symbolTable = createSymbolTable();
if (gCommandState.symbolTable == NULL) {
return -1;
}
gCommandState.commandStateInitialized = true;
return 0;
}
void
commandCleanup()
{
if (gCommandState.commandStateInitialized) {
gCommandState.commandStateInitialized = false;
deleteSymbolTable(gCommandState.symbolTable);
gCommandState.symbolTable = NULL;
//xxx need to free the entries and names in the symbol table
}
}
static int
registerCommandInternal(const char *name, CommandType type,
CommandArgumentType argType, CommandHook hook, void *cookie)
{
CommandEntry *entry;
if (!gCommandState.commandStateInitialized) {
return -1;
}
if (name == NULL || hook == NULL) {
return -1;
}
if (type != CMD_TYPE_COMMAND && type != CMD_TYPE_FUNCTION) {
return -1;
}
if (argType != CMD_ARGS_BOOLEAN && argType != CMD_ARGS_WORDS) {
return -1;
}
entry = (CommandEntry *)malloc(sizeof(CommandEntry));
if (entry != NULL) {
entry->name = strdup(name);
if (entry->name != NULL) {
int ret;
entry->cookie = cookie;
entry->type = type;
entry->argType = argType;
entry->hook = hook;
ret = addToSymbolTable(gCommandState.symbolTable,
entry->name, entry->type, entry);
if (ret == 0) {
return 0;
}
}
free(entry);
}
return -1;
}
int
registerCommand(const char *name,
CommandArgumentType argType, CommandHook hook, void *cookie)
{
return registerCommandInternal(name,
CMD_TYPE_COMMAND, argType, hook, cookie);
}
int
registerFunction(const char *name, FunctionHook hook, void *cookie)
{
return registerCommandInternal(name,
CMD_TYPE_FUNCTION, CMD_ARGS_WORDS, (CommandHook)hook, cookie);
}
Command *
findCommand(const char *name)
{
return (Command *)findInSymbolTable(gCommandState.symbolTable,
name, CMD_TYPE_COMMAND);
}
Function *
findFunction(const char *name)
{
return (Function *)findInSymbolTable(gCommandState.symbolTable,
name, CMD_TYPE_FUNCTION);
}
CommandArgumentType
getCommandArgumentType(Command *cmd)
{
CommandEntry *entry = (CommandEntry *)cmd;
if (entry != NULL) {
return entry->argType;
}
return CMD_ARGS_UNKNOWN;
}
static int
callCommandInternal(CommandEntry *entry, int argc, const char *argv[],
PermissionRequestList *permissions)
{
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;
}
}
}
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.
}
bail:
return -1;
}
static int
callBooleanCommandInternal(CommandEntry *entry, bool arg,
PermissionRequestList *permissions)
{
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 -1;
}
int
callCommand(Command *cmd, int argc, const char *argv[])
{
return callCommandInternal((CommandEntry *)cmd, argc, argv, NULL);
}
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;
}
int
callFunctionInternal(CommandEntry *entry, int argc, const char *argv[],
char **result, size_t *resultLen, PermissionRequestList *permissions)
{
if (entry != NULL && entry->argType == CMD_ARGS_WORDS &&
(argc == 0 || (argc > 0 && argv != NULL)))
{
if ((permissions == NULL && result != NULL) ||
(permissions != NULL && 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;
}
}
}
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.
}
}
bail:
return -1;
}
int
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;
}

96
amend/commands.h Normal file
View file

@ -0,0 +1,96 @@
/*
* 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_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.
*
* 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 commandInit(void);
void commandCleanup(void);
/*
* Command management
*/
struct Command;
typedef struct Command Command;
typedef enum {
CMD_ARGS_UNKNOWN = -1,
CMD_ARGS_BOOLEAN = 0,
CMD_ARGS_WORDS
} CommandArgumentType;
int registerCommand(const char *name,
CommandArgumentType argType, CommandHook hook, void *cookie);
Command *findCommand(const char *name);
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);
struct Function;
typedef struct Function Function;
int registerFunction(const char *name, FunctionHook hook, void *cookie);
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_

315
amend/execute.c Normal file
View file

@ -0,0 +1,315 @@
/*
* 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 <stdio.h>
#undef NDEBUG
#include <assert.h>
#include "ast.h"
#include "execute.h"
typedef struct {
int c;
const char **v;
} StringList;
static int execBooleanValue(ExecContext *ctx,
const AmBooleanValue *booleanValue, bool *result);
static int execStringValue(ExecContext *ctx, const AmStringValue *stringValue,
const char **result);
static int
execBooleanExpression(ExecContext *ctx,
const AmBooleanExpression *booleanExpression, bool *result)
{
int ret;
bool arg1, arg2;
bool unary;
assert(ctx != NULL);
assert(booleanExpression != NULL);
assert(result != NULL);
if (ctx == NULL || booleanExpression == NULL || result == NULL) {
return -__LINE__;
}
if (booleanExpression->op == AM_BOP_NOT) {
unary = true;
} else {
unary = false;
}
ret = execBooleanValue(ctx, booleanExpression->arg1, &arg1);
if (ret != 0) return ret;
if (!unary) {
ret = execBooleanValue(ctx, booleanExpression->arg2, &arg2);
if (ret != 0) return ret;
} else {
arg2 = false;
}
switch (booleanExpression->op) {
case AM_BOP_NOT:
*result = !arg1;
break;
case AM_BOP_EQ:
*result = (arg1 == arg2);
break;
case AM_BOP_NE:
*result = (arg1 != arg2);
break;
case AM_BOP_AND:
*result = (arg1 && arg2);
break;
case AM_BOP_OR:
*result = (arg1 || arg2);
break;
default:
return -__LINE__;
}
return 0;
}
static int
execFunctionArguments(ExecContext *ctx,
const AmFunctionArguments *functionArguments, StringList *result)
{
int ret;
assert(ctx != NULL);
assert(functionArguments != NULL);
assert(result != NULL);
if (ctx == NULL || functionArguments == NULL || result == NULL) {
return -__LINE__;
}
result->c = functionArguments->argc;
result->v = (const char **)malloc(result->c * sizeof(const char *));
if (result->v == NULL) {
result->c = 0;
return -__LINE__;
}
int i;
for (i = 0; i < functionArguments->argc; i++) {
ret = execStringValue(ctx, &functionArguments->argv[i], &result->v[i]);
if (ret != 0) {
result->c = 0;
free(result->v);
//TODO: free the individual args, if we're responsible for them.
result->v = NULL;
return ret;
}
}
return 0;
}
static int
execFunctionCall(ExecContext *ctx, const AmFunctionCall *functionCall,
const char **result)
{
int ret;
assert(ctx != NULL);
assert(functionCall != NULL);
assert(result != NULL);
if (ctx == NULL || functionCall == NULL || result == NULL) {
return -__LINE__;
}
StringList args;
ret = execFunctionArguments(ctx, functionCall->args, &args);
if (ret != 0) {
return ret;
}
ret = callFunction(functionCall->fn, args.c, args.v, (char **)result, NULL);
if (ret != 0) {
return ret;
}
//TODO: clean up args
return 0;
}
static int
execStringValue(ExecContext *ctx, const AmStringValue *stringValue,
const char **result)
{
int ret;
assert(ctx != NULL);
assert(stringValue != NULL);
assert(result != NULL);
if (ctx == NULL || stringValue == NULL || result == NULL) {
return -__LINE__;
}
switch (stringValue->type) {
case AM_SVAL_LITERAL:
*result = strdup(stringValue->u.literal);
break;
case AM_SVAL_FUNCTION:
ret = execFunctionCall(ctx, stringValue->u.function, result);
if (ret != 0) {
return ret;
}
break;
default:
return -__LINE__;
}
return 0;
}
static int
execStringComparisonExpression(ExecContext *ctx,
const AmStringComparisonExpression *stringComparisonExpression,
bool *result)
{
int ret;
assert(ctx != NULL);
assert(stringComparisonExpression != NULL);
assert(result != NULL);
if (ctx == NULL || stringComparisonExpression == NULL || result == NULL) {
return -__LINE__;
}
const char *arg1, *arg2;
ret = execStringValue(ctx, stringComparisonExpression->arg1, &arg1);
if (ret != 0) {
return ret;
}
ret = execStringValue(ctx, stringComparisonExpression->arg2, &arg2);
if (ret != 0) {
return ret;
}
int cmp = strcmp(arg1, arg2);
switch (stringComparisonExpression->op) {
case AM_SOP_LT:
*result = (cmp < 0);
break;
case AM_SOP_LE:
*result = (cmp <= 0);
break;
case AM_SOP_GT:
*result = (cmp > 0);
break;
case AM_SOP_GE:
*result = (cmp >= 0);
break;
case AM_SOP_EQ:
*result = (cmp == 0);
break;
case AM_SOP_NE:
*result = (cmp != 0);
break;
default:
return -__LINE__;
break;
}
return 0;
}
static int
execBooleanValue(ExecContext *ctx, const AmBooleanValue *booleanValue,
bool *result)
{
int ret;
assert(ctx != NULL);
assert(booleanValue != NULL);
assert(result != NULL);
if (ctx == NULL || booleanValue == NULL || result == NULL) {
return -__LINE__;
}
switch (booleanValue->type) {
case AM_BVAL_EXPRESSION:
ret = execBooleanExpression(ctx, &booleanValue->u.expression, result);
break;
case AM_BVAL_STRING_COMPARISON:
ret = execStringComparisonExpression(ctx,
&booleanValue->u.stringComparison, result);
break;
default:
ret = -__LINE__;
break;
}
return ret;
}
static int
execCommand(ExecContext *ctx, const AmCommand *command)
{
int ret;
assert(ctx != NULL);
assert(command != NULL);
if (ctx == NULL || command == NULL) {
return -__LINE__;
}
CommandArgumentType argType;
argType = getCommandArgumentType(command->cmd);
switch (argType) {
case CMD_ARGS_BOOLEAN:
{
bool bVal;
ret = execBooleanValue(ctx, command->args->u.b, &bVal);
if (ret == 0) {
ret = callBooleanCommand(command->cmd, bVal);
}
}
break;
case CMD_ARGS_WORDS:
{
AmWordList *words = command->args->u.w;
ret = callCommand(command->cmd, words->argc, words->argv);
}
break;
default:
ret = -__LINE__;
break;
}
return ret;
}
int
execCommandList(ExecContext *ctx, const AmCommandList *commandList)
{
int i;
for (i = 0; i < commandList->commandCount; i++) {
int ret = execCommand(ctx, commandList->commands[i]);
if (ret != 0) {
int line = commandList->commands[i]->line;
return line > 0 ? line : ret;
}
}
return 0;
}

25
amend/execute.h Normal file
View file

@ -0,0 +1,25 @@
/*
* 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_EXECUTE_H_
#define AMEND_EXECUTE_H_
typedef struct ExecContext ExecContext;
/* Returns 0 on success, otherwise the line number that failed. */
int execCommandList(ExecContext *ctx, const AmCommandList *commandList);
#endif // AMEND_EXECUTE_H_

43
amend/lexer.h Normal file
View file

@ -0,0 +1,43 @@
/*
* 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_LEXER_H_
#define AMEND_LEXER_H_
#define AMEND_LEXER_BUFFER_INPUT 1
void yyerror(const char *msg);
int yylex(void);
#if AMEND_LEXER_BUFFER_INPUT
void setLexerInputBuffer(const char *buf, size_t buflen);
#else
#include <stdio.h>
void yyset_in(FILE *in_str);
#endif
const char *tokenToString(int token);
typedef enum {
AM_UNKNOWN_ARGS,
AM_WORD_ARGS,
AM_BOOLEAN_ARGS,
} AmArgumentType;
void setLexerArgumentType(AmArgumentType type);
int getLexerLineNumber(void);
#endif // AMEND_LEXER_H_

299
amend/lexer.l Normal file
View file

@ -0,0 +1,299 @@
/*
* 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 <stdio.h>
#include <stdlib.h>
#include "ast.h"
#include "lexer.h"
#include "parser.h"
const char *tokenToString(int token)
{
static char scratch[128];
switch (token) {
case TOK_AND:
return "&&";
case TOK_OR:
return "||";
case TOK_EQ:
return "==";
case TOK_NE:
return "!=";
case TOK_GE:
return ">=";
case TOK_LE:
return "<=";
case TOK_EOF:
return "EOF";
case TOK_EOL:
return "EOL\n";
case TOK_STRING:
snprintf(scratch, sizeof(scratch),
"STRING<%s>", yylval.literalString);
return scratch;
case TOK_IDENTIFIER:
snprintf(scratch, sizeof(scratch), "IDENTIFIER<%s>",
yylval.literalString);
return scratch;
case TOK_WORD:
snprintf(scratch, sizeof(scratch), "WORD<%s>",
yylval.literalString);
return scratch;
default:
if (token > ' ' && token <= '~') {
scratch[0] = (char)token;
scratch[1] = '\0';
} else {
snprintf(scratch, sizeof(scratch), "??? <%d>", token);
}
return scratch;
}
}
typedef struct {
char *value;
char *nextc;
unsigned int alloc_size;
} AmString;
static int addCharToString(AmString *str, char c)
{
if ((unsigned int)(str->nextc - str->value) >= str->alloc_size) {
char *new_value;
unsigned int new_size;
new_size = (str->alloc_size + 1) * 2;
if (new_size < 64) {
new_size = 64;
}
new_value = (char *)realloc(str->value, new_size);
if (new_value == NULL) {
yyerror("out of memory");
return -1;
}
str->nextc = str->nextc - str->value + new_value;
str->value = new_value;
str->alloc_size = new_size;
}
*str->nextc++ = c;
return 0;
}
static int setString(AmString *str, const char *p)
{
str->nextc = str->value;
while (*p != '\0') {
//TODO: add the whole string at once
addCharToString(str, *p++);
}
return addCharToString(str, '\0');
}
static AmString gStr = { NULL, NULL, 0 };
static int gLineNumber = 1;
static AmArgumentType gArgumentType = AM_UNKNOWN_ARGS;
static const char *gErrorMessage = NULL;
#if AMEND_LEXER_BUFFER_INPUT
static const char *gInputBuffer;
static const char *gInputBufferNext;
static const char *gInputBufferEnd;
# define YY_INPUT(buf, result, max_size) \
do { \
int nbytes = gInputBufferEnd - gInputBufferNext; \
if (nbytes > 0) { \
if (nbytes > max_size) { \
nbytes = max_size; \
} \
memcpy(buf, gInputBufferNext, nbytes); \
gInputBufferNext += nbytes; \
result = nbytes; \
} else { \
result = YY_NULL; \
} \
} while (false)
#endif // AMEND_LEXER_BUFFER_INPUT
%}
%option noyywrap
%x QUOTED_STRING BOOLEAN WORDS
ident [a-zA-Z_][a-zA-Z_0-9]*
word [^ \t\r\n"]+
%%
/* This happens at the beginning of each call to yylex().
*/
if (gArgumentType == AM_WORD_ARGS) {
BEGIN(WORDS);
} else if (gArgumentType == AM_BOOLEAN_ARGS) {
BEGIN(BOOLEAN);
}
/*xxx require everything to be 7-bit-clean, printable characters */
<INITIAL>{
{ident}/[ \t\r\n] {
/* The only token we recognize in the initial
* state is an identifier followed by whitespace.
*/
setString(&gStr, yytext);
yylval.literalString = gStr.value;
return TOK_IDENTIFIER;
}
}
<BOOLEAN>{
{ident} {
/* Non-quoted identifier-style string */
setString(&gStr, yytext);
yylval.literalString = gStr.value;
return TOK_IDENTIFIER;
}
"&&" return TOK_AND;
"||" return TOK_OR;
"==" return TOK_EQ;
"!=" return TOK_NE;
">=" return TOK_GE;
"<=" return TOK_LE;
[<>()!,] return yytext[0];
}
/* Double-quoted string handling */
<WORDS,BOOLEAN>\" {
/* Initial quote */
gStr.nextc = gStr.value;
BEGIN(QUOTED_STRING);
}
<QUOTED_STRING>{
\" {
/* Closing quote */
BEGIN(INITIAL);
addCharToString(&gStr, '\0');
yylval.literalString = gStr.value;
if (gArgumentType == AM_WORD_ARGS) {
return TOK_WORD;
} else {
return TOK_STRING;
}
}
<<EOF>> |
\n {
/* Unterminated string */
yyerror("unterminated string");
return TOK_ERROR;
}
\\\" {
/* Escaped quote */
addCharToString(&gStr, '"');
}
\\\\ {
/* Escaped backslash */
addCharToString(&gStr, '\\');
}
\\. {
/* No other escapes allowed. */
gErrorMessage = "illegal escape";
return TOK_ERROR;
}
[^\\\n\"]+ {
/* String contents */
char *p = yytext;
while (*p != '\0') {
/* TODO: add the whole string at once */
addCharToString(&gStr, *p++);
}
}
}
<WORDS>{
/*xxx look out for backslashes; escape backslashes and quotes */
/*xxx if a quote is right against a char, we should append */
{word} {
/* Whitespace-separated word */
setString(&gStr, yytext);
yylval.literalString = gStr.value;
return TOK_WORD;
}
}
<INITIAL,WORDS,BOOLEAN>{
\n {
/* Count lines */
gLineNumber++;
gArgumentType = AM_UNKNOWN_ARGS;
BEGIN(INITIAL);
return TOK_EOL;
}
/*xxx backslashes to extend lines? */
/* Skip whitespace and comments.
*/
[ \t\r]+ ;
#.* ;
. {
/* Fail on anything we didn't expect. */
gErrorMessage = "unexpected character";
return TOK_ERROR;
}
}
%%
void
yyerror(const char *msg)
{
if (!strcmp(msg, "syntax error") && gErrorMessage != NULL) {
msg = gErrorMessage;
gErrorMessage = NULL;
}
fprintf(stderr, "line %d: %s at '%s'\n", gLineNumber, msg, yytext);
}
#if AMEND_LEXER_BUFFER_INPUT
void
setLexerInputBuffer(const char *buf, size_t buflen)
{
gLineNumber = 1;
gInputBuffer = buf;
gInputBufferNext = gInputBuffer;
gInputBufferEnd = gInputBuffer + buflen;
}
#endif // AMEND_LEXER_BUFFER_INPUT
void
setLexerArgumentType(AmArgumentType type)
{
gArgumentType = type;
}
int
getLexerLineNumber(void)
{
return gLineNumber;
}

195
amend/main.c Normal file
View file

@ -0,0 +1,195 @@
/*
* 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>
#include "ast.h"
#include "lexer.h"
#include "parser.h"
#include "register.h"
#include "execute.h"
void
lexTest()
{
int token;
do {
token = yylex();
if (token == 0) {
printf(" EOF");
fflush(stdout);
break;
} else {
printf(" %s", tokenToString(token));
fflush(stdout);
if (token == TOK_IDENTIFIER) {
if (strcmp(yylval.literalString, "assert") == 0) {
setLexerArgumentType(AM_BOOLEAN_ARGS);
} else {
setLexerArgumentType(AM_WORD_ARGS);
}
do {
token = yylex();
printf(" %s", tokenToString(token));
fflush(stdout);
} while (token != TOK_EOL && token != TOK_EOF && token != 0);
} else if (token != TOK_EOL) {
fprintf(stderr, "syntax error: expected identifier\n");
break;
}
}
} while (token != 0);
printf("\n");
}
void
usage()
{
printf("usage: amend [--debug-lex|--debug-ast] [<filename>]\n");
exit(1);
}
extern const AmCommandList *gCommands;
int
main(int argc, char *argv[])
{
FILE *inputFile = NULL;
bool debugLex = false;
bool debugAst = false;
const char *fileName = NULL;
int err;
#if 1
extern int test_symtab(void);
int ret = test_symtab();
if (ret != 0) {
fprintf(stderr, "test_symtab() failed: %d\n", ret);
exit(ret);
}
extern int test_cmd_fn(void);
ret = test_cmd_fn();
if (ret != 0) {
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--;
argv++;
while (argc > 0) {
if (strcmp("--debug-lex", argv[0]) == 0) {
debugLex = true;
} else if (strcmp("--debug-ast", argv[0]) == 0) {
debugAst = true;
} else if (argv[0][0] == '-') {
fprintf(stderr, "amend: Unknown option \"%s\"\n", argv[0]);
usage();
} else {
fileName = argv[0];
}
argc--;
argv++;
}
if (fileName != NULL) {
inputFile = fopen(fileName, "r");
if (inputFile == NULL) {
fprintf(stderr, "amend: Can't open input file '%s'\n", fileName);
usage();
}
}
commandInit();
//xxx clean up
err = registerUpdateCommands();
if (err < 0) {
fprintf(stderr, "amend: Error registering commands: %d\n", err);
exit(-err);
}
err = registerUpdateFunctions();
if (err < 0) {
fprintf(stderr, "amend: Error registering functions: %d\n", err);
exit(-err);
}
#if AMEND_LEXER_BUFFER_INPUT
if (inputFile == NULL) {
fprintf(stderr, "amend: No input file\n");
usage();
}
char *fileData;
int fileDataLen;
fseek(inputFile, 0, SEEK_END);
fileDataLen = ftell(inputFile);
rewind(inputFile);
if (fileDataLen < 0) {
fprintf(stderr, "amend: Can't get file length\n");
exit(2);
} else if (fileDataLen == 0) {
printf("amend: Empty input file\n");
exit(0);
}
fileData = (char *)malloc(fileDataLen + 1);
if (fileData == NULL) {
fprintf(stderr, "amend: Can't allocate %d bytes\n", fileDataLen + 1);
exit(2);
}
size_t nread = fread(fileData, 1, fileDataLen, inputFile);
if (nread != (size_t)fileDataLen) {
fprintf(stderr, "amend: Didn't read %d bytes, only %zd\n", fileDataLen,
nread);
exit(2);
}
fileData[fileDataLen] = '\0';
setLexerInputBuffer(fileData, fileDataLen);
#else
if (inputFile == NULL) {
inputFile = stdin;
}
yyset_in(inputFile);
#endif
if (debugLex) {
lexTest();
} else {
int ret = yyparse();
if (ret != 0) {
fprintf(stderr, "amend: Parse failed (%d)\n", ret);
exit(2);
} else {
if (debugAst) {
dumpCommandList(gCommands);
}
printf("amend: Parse successful.\n");
ret = execCommandList((ExecContext *)1, gCommands);
if (ret != 0) {
fprintf(stderr, "amend: Execution failed (%d)\n", ret);
exit(3);
}
printf("amend: Execution successful.\n");
}
}
return 0;
}

24
amend/parser.h Normal file
View file

@ -0,0 +1,24 @@
/*
* 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_PARSER_H_
#define AMEND_PARSER_H_
#include "parser_y.h"
int yyparse(void);
#endif // AMEND_PARSER_H_

430
amend/parser_y.y Normal file
View file

@ -0,0 +1,430 @@
/*
* 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.
*/
%{
#undef NDEBUG
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include "ast.h"
#include "lexer.h"
#include "commands.h"
void yyerror(const char *msg);
int yylex(void);
#define STRING_COMPARISON(out, a1, sop, a2) \
do { \
out = (AmBooleanValue *)malloc(sizeof(AmBooleanValue)); \
if (out == NULL) { \
YYABORT; \
} \
out->type = AM_BVAL_STRING_COMPARISON; \
out->u.stringComparison.op = sop; \
out->u.stringComparison.arg1 = a1; \
out->u.stringComparison.arg2 = a2; \
} while (false)
#define BOOLEAN_EXPRESSION(out, a1, bop, a2) \
do { \
out = (AmBooleanValue *)malloc(sizeof(AmBooleanValue)); \
if (out == NULL) { \
YYABORT; \
} \
out->type = AM_BVAL_EXPRESSION; \
out->u.expression.op = bop; \
out->u.expression.arg1 = a1; \
out->u.expression.arg2 = a2; \
} while (false)
AmCommandList *gCommands = NULL;
%}
%start lines
%union {
char *literalString;
AmFunctionArgumentBuilder *functionArgumentBuilder;
AmFunctionArguments *functionArguments;
AmFunctionCall *functionCall;
AmStringValue *stringValue;
AmBooleanValue *booleanValue;
AmWordListBuilder *wordListBuilder;
AmCommandArguments *commandArguments;
AmCommand *command;
AmCommandList *commandList;
}
%token TOK_AND TOK_OR TOK_EQ TOK_NE TOK_GE TOK_LE TOK_EOF TOK_EOL TOK_ERROR
%token <literalString> TOK_STRING TOK_IDENTIFIER TOK_WORD
%type <commandList> lines
%type <command> command line
%type <functionArgumentBuilder> function_arguments
%type <functionArguments> function_arguments_or_empty
%type <functionCall> function_call
%type <literalString> function_name
%type <stringValue> string_value
%type <booleanValue> boolean_expression
%type <wordListBuilder> word_list
%type <commandArguments> arguments
/* Operator precedence, weakest to strongest.
* Same as C/Java precedence.
*/
%left TOK_OR
%left TOK_AND
%left TOK_EQ TOK_NE
%left '<' '>' TOK_LE TOK_GE
%right '!'
%%
lines : /* empty */
{
$$ = (AmCommandList *)malloc(sizeof(AmCommandList));
if ($$ == NULL) {
YYABORT;
}
gCommands = $$;
$$->arraySize = 64;
$$->commandCount = 0;
$$->commands = (AmCommand **)malloc(
sizeof(AmCommand *) * $$->arraySize);
if ($$->commands == NULL) {
YYABORT;
}
}
| lines line
{
if ($2 != NULL) {
if ($1->commandCount >= $1->arraySize) {
AmCommand **newArray;
newArray = (AmCommand **)realloc($$->commands,
sizeof(AmCommand *) * $$->arraySize * 2);
if (newArray == NULL) {
YYABORT;
}
$$->commands = newArray;
$$->arraySize *= 2;
}
$1->commands[$1->commandCount++] = $2;
}
}
;
line : line_ending
{
$$ = NULL; /* ignore blank lines */
}
| command arguments line_ending
{
$$ = $1;
$$->args = $2;
setLexerArgumentType(AM_UNKNOWN_ARGS);
}
;
command : TOK_IDENTIFIER
{
Command *cmd = findCommand($1);
if (cmd == NULL) {
fprintf(stderr, "Unknown command \"%s\"\n", $1);
YYABORT;
}
$$ = (AmCommand *)malloc(sizeof(AmCommand));
if ($$ == NULL) {
YYABORT;
}
$$->line = getLexerLineNumber();
$$->name = strdup($1);
if ($$->name == NULL) {
YYABORT;
}
$$->args = NULL;
CommandArgumentType argType = getCommandArgumentType(cmd);
if (argType == CMD_ARGS_BOOLEAN) {
setLexerArgumentType(AM_BOOLEAN_ARGS);
} else {
setLexerArgumentType(AM_WORD_ARGS);
}
$$->cmd = cmd;
}
;
line_ending :
TOK_EOL
| TOK_EOF
;
arguments : boolean_expression
{
$$ = (AmCommandArguments *)malloc(
sizeof(AmCommandArguments));
if ($$ == NULL) {
YYABORT;
}
$$->booleanArgs = true;
$$->u.b = $1;
}
| word_list
{
/* Convert the builder list into an array.
* Do it in reverse order; the words were pushed
* onto the list in LIFO order.
*/
AmWordList *w = (AmWordList *)malloc(sizeof(AmWordList));
if (w == NULL) {
YYABORT;
}
if ($1 != NULL) {
AmWordListBuilder *words = $1;
w->argc = words->wordCount;
w->argv = (const char **)malloc(w->argc *
sizeof(char *));
if (w->argv == NULL) {
YYABORT;
}
int i;
for (i = w->argc; words != NULL && i > 0; --i) {
AmWordListBuilder *f = words;
w->argv[i-1] = words->word;
words = words->next;
free(f);
}
assert(i == 0);
assert(words == NULL);
} else {
w->argc = 0;
w->argv = NULL;
}
$$ = (AmCommandArguments *)malloc(
sizeof(AmCommandArguments));
if ($$ == NULL) {
YYABORT;
}
$$->booleanArgs = false;
$$->u.w = w;
}
;
word_list : /* empty */
{ $$ = NULL; }
| word_list TOK_WORD
{
if ($1 == NULL) {
$$ = (AmWordListBuilder *)malloc(
sizeof(AmWordListBuilder));
if ($$ == NULL) {
YYABORT;
}
$$->next = NULL;
$$->wordCount = 1;
} else {
$$ = (AmWordListBuilder *)malloc(
sizeof(AmWordListBuilder));
if ($$ == NULL) {
YYABORT;
}
$$->next = $1;
$$->wordCount = $$->next->wordCount + 1;
}
$$->word = strdup($2);
if ($$->word == NULL) {
YYABORT;
}
}
;
boolean_expression :
'!' boolean_expression
{
$$ = (AmBooleanValue *)malloc(sizeof(AmBooleanValue));
if ($$ == NULL) {
YYABORT;
}
$$->type = AM_BVAL_EXPRESSION;
$$->u.expression.op = AM_BOP_NOT;
$$->u.expression.arg1 = $2;
$$->u.expression.arg2 = NULL;
}
/* TODO: if both expressions are literals, evaluate now */
| boolean_expression TOK_AND boolean_expression
{ BOOLEAN_EXPRESSION($$, $1, AM_BOP_AND, $3); }
| boolean_expression TOK_OR boolean_expression
{ BOOLEAN_EXPRESSION($$, $1, AM_BOP_OR, $3); }
| boolean_expression TOK_EQ boolean_expression
{ BOOLEAN_EXPRESSION($$, $1, AM_BOP_EQ, $3); }
| boolean_expression TOK_NE boolean_expression
{ BOOLEAN_EXPRESSION($$, $1, AM_BOP_NE, $3); }
| '(' boolean_expression ')'
{ $$ = $2; }
/* TODO: if both strings are literals, evaluate now */
| string_value '<' string_value
{ STRING_COMPARISON($$, $1, AM_SOP_LT, $3); }
| string_value '>' string_value
{ STRING_COMPARISON($$, $1, AM_SOP_GT, $3); }
| string_value TOK_EQ string_value
{ STRING_COMPARISON($$, $1, AM_SOP_EQ, $3); }
| string_value TOK_NE string_value
{ STRING_COMPARISON($$, $1, AM_SOP_NE, $3); }
| string_value TOK_LE string_value
{ STRING_COMPARISON($$, $1, AM_SOP_LE, $3); }
| string_value TOK_GE string_value
{ STRING_COMPARISON($$, $1, AM_SOP_GE, $3); }
;
string_value :
TOK_IDENTIFIER
{
$$ = (AmStringValue *)malloc(sizeof(AmStringValue));
if ($$ == NULL) {
YYABORT;
}
$$->type = AM_SVAL_LITERAL;
$$->u.literal = strdup($1);
if ($$->u.literal == NULL) {
YYABORT;
}
}
| TOK_STRING
{
$$ = (AmStringValue *)malloc(sizeof(AmStringValue));
if ($$ == NULL) {
YYABORT;
}
$$->type = AM_SVAL_LITERAL;
$$->u.literal = strdup($1);
if ($$->u.literal == NULL) {
YYABORT;
}
}
| function_call
{
$$ = (AmStringValue *)malloc(sizeof(AmStringValue));
if ($$ == NULL) {
YYABORT;
}
$$->type = AM_SVAL_FUNCTION;
$$->u.function = $1;
}
;
/* We can't just say
* TOK_IDENTIFIER '(' function_arguments_or_empty ')'
* because parsing function_arguments_or_empty will clobber
* the underlying string that yylval.literalString points to.
*/
function_call :
function_name '(' function_arguments_or_empty ')'
{
Function *fn = findFunction($1);
if (fn == NULL) {
fprintf(stderr, "Unknown function \"%s\"\n", $1);
YYABORT;
}
$$ = (AmFunctionCall *)malloc(sizeof(AmFunctionCall));
if ($$ == NULL) {
YYABORT;
}
$$->name = $1;
if ($$->name == NULL) {
YYABORT;
}
$$->fn = fn;
$$->args = $3;
}
;
function_name :
TOK_IDENTIFIER
{
$$ = strdup($1);
}
;
function_arguments_or_empty :
/* empty */
{
$$ = (AmFunctionArguments *)malloc(
sizeof(AmFunctionArguments));
if ($$ == NULL) {
YYABORT;
}
$$->argc = 0;
$$->argv = NULL;
}
| function_arguments
{
AmFunctionArgumentBuilder *args = $1;
assert(args != NULL);
/* Convert the builder list into an array.
* Do it in reverse order; the args were pushed
* onto the list in LIFO order.
*/
$$ = (AmFunctionArguments *)malloc(
sizeof(AmFunctionArguments));
if ($$ == NULL) {
YYABORT;
}
$$->argc = args->argCount;
$$->argv = (AmStringValue *)malloc(
$$->argc * sizeof(AmStringValue));
if ($$->argv == NULL) {
YYABORT;
}
int i;
for (i = $$->argc; args != NULL && i > 0; --i) {
AmFunctionArgumentBuilder *f = args;
$$->argv[i-1] = *args->arg;
args = args->next;
free(f->arg);
free(f);
}
assert(i == 0);
assert(args == NULL);
}
;
function_arguments :
string_value
{
$$ = (AmFunctionArgumentBuilder *)malloc(
sizeof(AmFunctionArgumentBuilder));
if ($$ == NULL) {
YYABORT;
}
$$->next = NULL;
$$->argCount = 1;
$$->arg = $1;
}
| function_arguments ',' string_value
{
$$ = (AmFunctionArgumentBuilder *)malloc(
sizeof(AmFunctionArgumentBuilder));
if ($$ == NULL) {
YYABORT;
}
$$->next = $1;
$$->argCount = $$->next->argCount + 1;
$$->arg = $3;
}
;
/* xxx this whole tool needs to be hardened */

270
amend/permissions.c Normal file
View file

@ -0,0 +1,270 @@
/*
* 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;
}

111
amend/permissions.h Normal file
View file

@ -0,0 +1,111 @@
/*
* 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_

394
amend/register.c Normal file
View file

@ -0,0 +1,394 @@
/*
* 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 "commands.h"
#include "register.h"
#define UNUSED(p) ((void)(p))
#define CHECK_BOOL() \
do { \
assert(argv == NULL); \
if (argv != NULL) return -1; \
assert(argc == true || argc == false); \
if (argc != true && argc != false) return -1; \
} while (false)
#define CHECK_WORDS() \
do { \
assert(argc >= 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_; \
} \
} \
} while (false)
/*
* Command definitions
*/
/* assert <boolexpr>
*/
static int
cmd_assert(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
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)
*/
if (argc) {
return 0;
} else {
return 1;
}
}
/* format <root>
*/
static int
cmd_format(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_WORDS();
//xxx
return -1;
}
/* copy_dir <srcdir> <dstdir>
*/
static int
cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_WORDS();
//xxx
return -1;
}
/* mark <resource> dirty|clean
*/
static int
cmd_mark(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_WORDS();
//xxx when marking, save the top-level hash at the mark point
// so we can retry on failure. Otherwise the hashes won't match,
// or someone could intentionally dirty the FS to force a downgrade
//xxx
return -1;
}
/* done
*/
static int
cmd_done(const char *name, void *cookie, int argc, const char *argv[],
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_WORDS();
//xxx
return -1;
}
int
registerUpdateCommands()
{
int ret;
ret = registerCommand("assert", CMD_ARGS_BOOLEAN, cmd_assert, NULL);
if (ret < 0) return ret;
ret = registerCommand("copy_dir", CMD_ARGS_WORDS, cmd_copy_dir, NULL);
if (ret < 0) return ret;
ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, NULL);
if (ret < 0) return ret;
ret = registerCommand("mark", CMD_ARGS_WORDS, cmd_mark, NULL);
if (ret < 0) return ret;
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;
}
/*
* Function definitions
*/
/* update_forced()
*
* Returns "true" if some system setting has determined that
* the update should happen no matter what.
*/
static int
fn_update_forced(const char *name, void *cookie, int argc, const char *argv[],
char **result, size_t *resultLen,
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_FN();
NO_PERMS(permissions);
if (argc != 0) {
fprintf(stderr, "%s: wrong number of arguments (%d)\n",
name, argc);
return 1;
}
//xxx check some global or property
bool force = true;
if (force) {
*result = strdup("true");
} else {
*result = strdup("");
}
if (resultLen != NULL) {
*resultLen = strlen(*result);
}
return 0;
}
/* get_mark(<resource>)
*
* Returns the current mark associated with the provided resource.
*/
static int
fn_get_mark(const char *name, void *cookie, int argc, const char *argv[],
char **result, size_t *resultLen,
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_FN();
NO_PERMS(permissions);
if (argc != 1) {
fprintf(stderr, "%s: wrong number of arguments (%d)\n",
name, argc);
return 1;
}
//xxx look up the value
*result = strdup("");
if (resultLen != NULL) {
*resultLen = strlen(*result);
}
return 0;
}
/* hash_dir(<path-to-directory>)
*/
static int
fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[],
char **result, size_t *resultLen,
PermissionRequestList *permissions)
{
int ret = -1;
UNUSED(name);
UNUSED(cookie);
CHECK_FN();
const char *dir;
if (argc != 1) {
fprintf(stderr, "%s: wrong number of arguments (%d)\n",
name, argc);
return 1;
} else {
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;
}
/* matches(<str>, <str1> [, <strN>...])
* If <str> matches (strcmp) any of <str1>...<strN>, returns <str>,
* otherwise returns "".
*
* E.g., assert matches(hash_dir("/path"), "hash1", "hash2")
*/
static int
fn_matches(const char *name, void *cookie, int argc, const char *argv[],
char **result, size_t *resultLen,
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_FN();
NO_PERMS(permissions);
if (argc < 2) {
fprintf(stderr, "%s: not enough arguments (%d < 2)\n",
name, argc);
return 1;
}
int i;
for (i = 1; i < argc; i++) {
if (strcmp(argv[0], argv[i]) == 0) {
*result = strdup(argv[0]);
if (resultLen != NULL) {
*resultLen = strlen(*result);
}
return 0;
}
}
*result = strdup("");
if (resultLen != NULL) {
*resultLen = 1;
}
return 0;
}
/* concat(<str>, <str1> [, <strN>...])
* Returns the concatenation of all strings.
*/
static int
fn_concat(const char *name, void *cookie, int argc, const char *argv[],
char **result, size_t *resultLen,
PermissionRequestList *permissions)
{
UNUSED(name);
UNUSED(cookie);
CHECK_FN();
NO_PERMS(permissions);
size_t totalLen = 0;
int i;
for (i = 0; i < argc; i++) {
totalLen += strlen(argv[i]);
}
char *s = (char *)malloc(totalLen + 1);
if (s == NULL) {
return -1;
}
s[totalLen] = '\0';
for (i = 0; i < argc; i++) {
//TODO: keep track of the end to avoid walking the string each time
strcat(s, argv[i]);
}
*result = s;
if (resultLen != NULL) {
*resultLen = strlen(s);
}
return 0;
}
int
registerUpdateFunctions()
{
int ret;
ret = registerFunction("update_forced", fn_update_forced, NULL);
if (ret < 0) return ret;
ret = registerFunction("get_mark", fn_get_mark, NULL);
if (ret < 0) return ret;
ret = registerFunction("hash_dir", fn_hash_dir, NULL);
if (ret < 0) return ret;
ret = registerFunction("matches", fn_matches, NULL);
if (ret < 0) return ret;
ret = registerFunction("concat", fn_concat, NULL);
if (ret < 0) return ret;
return 0;
}

23
amend/register.h Normal file
View file

@ -0,0 +1,23 @@
/*
* 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_REGISTER_H_
#define AMEND_REGISTER_H_
int registerUpdateCommands(void);
int registerUpdateFunctions(void);
#endif // AMEND_REGISTER_H_

132
amend/symtab.c Normal file
View file

@ -0,0 +1,132 @@
/*
* 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 "symtab.h"
#define DEFAULT_TABLE_SIZE 16
typedef struct {
char *symbol;
const void *cookie;
unsigned int flags;
} SymbolTableEntry;
struct SymbolTable {
SymbolTableEntry *table;
int numEntries;
int maxSize;
};
SymbolTable *
createSymbolTable()
{
SymbolTable *tab;
tab = (SymbolTable *)malloc(sizeof(SymbolTable));
if (tab != NULL) {
tab->numEntries = 0;
tab->maxSize = DEFAULT_TABLE_SIZE;
tab->table = (SymbolTableEntry *)malloc(
tab->maxSize * sizeof(SymbolTableEntry));
if (tab->table == NULL) {
free(tab);
tab = NULL;
}
}
return tab;
}
void
deleteSymbolTable(SymbolTable *tab)
{
if (tab != NULL) {
while (tab->numEntries > 0) {
free(tab->table[--tab->numEntries].symbol);
}
free(tab->table);
}
}
void *
findInSymbolTable(SymbolTable *tab, const char *symbol, unsigned int flags)
{
int i;
if (tab == NULL || symbol == NULL) {
return NULL;
}
// TODO: Sort the table and binary search
for (i = 0; i < tab->numEntries; i++) {
if (strcmp(tab->table[i].symbol, symbol) == 0 &&
tab->table[i].flags == flags)
{
return (void *)tab->table[i].cookie;
}
}
return NULL;
}
int
addToSymbolTable(SymbolTable *tab, const char *symbol, unsigned int flags,
const void *cookie)
{
if (tab == NULL || symbol == NULL || cookie == NULL) {
return -1;
}
/* Make sure that this symbol isn't already in the table.
*/
if (findInSymbolTable(tab, symbol, flags) != NULL) {
return -2;
}
/* Make sure there's enough space for the new entry.
*/
if (tab->numEntries == tab->maxSize) {
SymbolTableEntry *newTable;
int newSize;
newSize = tab->numEntries * 2;
if (newSize < DEFAULT_TABLE_SIZE) {
newSize = DEFAULT_TABLE_SIZE;
}
newTable = (SymbolTableEntry *)realloc(tab->table,
newSize * sizeof(SymbolTableEntry));
if (newTable == NULL) {
return -1;
}
tab->maxSize = newSize;
tab->table = newTable;
}
/* Insert the new entry.
*/
symbol = strdup(symbol);
if (symbol == NULL) {
return -1;
}
// TODO: Sort the table
tab->table[tab->numEntries].symbol = (char *)symbol;
tab->table[tab->numEntries].cookie = cookie;
tab->table[tab->numEntries].flags = flags;
tab->numEntries++;
return 0;
}

34
amend/symtab.h Normal file
View file

@ -0,0 +1,34 @@
/*
* 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_SYMTAB_H_
#define AMEND_SYMTAB_H_
typedef struct SymbolTable SymbolTable;
SymbolTable *createSymbolTable(void);
void deleteSymbolTable(SymbolTable *tab);
/* symbol and cookie must be non-NULL.
*/
int addToSymbolTable(SymbolTable *tab, const char *symbol, unsigned int flags,
const void *cookie);
void *findInSymbolTable(SymbolTable *tab, const char *symbol,
unsigned int flags);
#endif // AMEND_SYMTAB_H_

538
amend/test_commands.c Normal file
View file

@ -0,0 +1,538 @@
/*
* 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 <stdio.h>
#undef NDEBUG
#include <assert.h>
#include "commands.h"
static struct {
bool called;
const char *name;
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)
{
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)
{
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) {
*resultLen = strlen(*result);
}
}
return gTestCommandState.returnValue;
}
static int
test_commands()
{
Command *cmd;
int ret;
CommandArgumentType argType;
ret = commandInit();
assert(ret == 0);
/* Make sure we can't initialize twice.
*/
ret = commandInit();
assert(ret < 0);
/* Try calling with some bad values.
*/
ret = registerCommand(NULL, CMD_ARGS_UNKNOWN, NULL, NULL);
assert(ret < 0);
ret = registerCommand("hello", CMD_ARGS_UNKNOWN, NULL, NULL);
assert(ret < 0);
ret = registerCommand("hello", CMD_ARGS_WORDS, NULL, NULL);
assert(ret < 0);
cmd = findCommand(NULL);
assert(cmd == NULL);
argType = getCommandArgumentType(NULL);
assert((int)argType < 0);
ret = callCommand(NULL, -1, NULL);
assert(ret < 0);
ret = callBooleanCommand(NULL, false);
assert(ret < 0);
/* Register some commands.
*/
ret = registerCommand("one", CMD_ARGS_WORDS, testCommand,
&gTestCommandState);
assert(ret == 0);
ret = registerCommand("two", CMD_ARGS_WORDS, testCommand,
&gTestCommandState);
assert(ret == 0);
ret = registerCommand("bool", CMD_ARGS_BOOLEAN, testCommand,
&gTestCommandState);
assert(ret == 0);
/* Make sure that all of those commands exist and that their
* argument types are correct.
*/
cmd = findCommand("one");
assert(cmd != NULL);
argType = getCommandArgumentType(cmd);
assert(argType == CMD_ARGS_WORDS);
cmd = findCommand("two");
assert(cmd != NULL);
argType = getCommandArgumentType(cmd);
assert(argType == CMD_ARGS_WORDS);
cmd = findCommand("bool");
assert(cmd != NULL);
argType = getCommandArgumentType(cmd);
assert(argType == CMD_ARGS_BOOLEAN);
/* Make sure that no similar commands exist.
*/
cmd = findCommand("on");
assert(cmd == NULL);
cmd = findCommand("onee");
assert(cmd == NULL);
/* Make sure that a double insertion fails.
*/
ret = registerCommand("one", CMD_ARGS_WORDS, testCommand,
&gTestCommandState);
assert(ret < 0);
/* Make sure that bad args fail.
*/
cmd = findCommand("one");
assert(cmd != NULL);
ret = callCommand(cmd, -1, NULL); // argc must be non-negative
assert(ret < 0);
ret = callCommand(cmd, 1, NULL); // argv can't be NULL if argc > 0
assert(ret < 0);
/* Make sure that you can't make a boolean call on a regular command.
*/
cmd = findCommand("one");
assert(cmd != NULL);
ret = callBooleanCommand(cmd, false);
assert(ret < 0);
/* Make sure that you can't make a regular call on a boolean command.
*/
cmd = findCommand("bool");
assert(cmd != NULL);
ret = callCommand(cmd, 0, NULL);
assert(ret < 0);
/* Set up some arguments.
*/
int argc = 4;
const char *argv[4] = { "ONE", "TWO", "THREE", "FOUR" };
/* Make a call and make sure that it occurred.
*/
cmd = findCommand("one");
assert(cmd != NULL);
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);
assert(gTestCommandState.called);
assert(strcmp(gTestCommandState.name, "one") == 0);
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.
*/
cmd = findCommand("bool");
assert(cmd != NULL);
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);
assert(strcmp(gTestCommandState.name, "bool") == 0);
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);
assert(strcmp(gTestCommandState.name, "bool") == 0);
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().
*/
commandCleanup();
return 0;
}
static int
test_functions()
{
Function *fn;
int ret;
ret = commandInit();
assert(ret == 0);
/* Try calling with some bad values.
*/
ret = registerFunction(NULL, NULL, NULL);
assert(ret < 0);
ret = registerFunction("hello", NULL, NULL);
assert(ret < 0);
fn = findFunction(NULL);
assert(fn == NULL);
ret = callFunction(NULL, -1, NULL, NULL, NULL);
assert(ret < 0);
/* Register some functions.
*/
ret = registerFunction("one", testFunction, &gTestCommandState);
assert(ret == 0);
ret = registerFunction("two", testFunction, &gTestCommandState);
assert(ret == 0);
ret = registerFunction("three", testFunction, &gTestCommandState);
assert(ret == 0);
/* Make sure that all of those functions exist.
* argument types are correct.
*/
fn = findFunction("one");
assert(fn != NULL);
fn = findFunction("two");
assert(fn != NULL);
fn = findFunction("three");
assert(fn != NULL);
/* Make sure that no similar functions exist.
*/
fn = findFunction("on");
assert(fn == NULL);
fn = findFunction("onee");
assert(fn == NULL);
/* Make sure that a double insertion fails.
*/
ret = registerFunction("one", testFunction, &gTestCommandState);
assert(ret < 0);
/* Make sure that bad args fail.
*/
fn = findFunction("one");
assert(fn != NULL);
// argc must be non-negative
ret = callFunction(fn, -1, NULL, (char **)1, NULL);
assert(ret < 0);
// argv can't be NULL if argc > 0
ret = callFunction(fn, 1, NULL, (char **)1, NULL);
assert(ret < 0);
// result can't be NULL
ret = callFunction(fn, 0, NULL, NULL, NULL);
assert(ret < 0);
/* Set up some arguments.
*/
int argc = 4;
const char *argv[4] = { "ONE", "TWO", "THREE", "FOUR" };
/* Make a call and make sure that it occurred.
*/
char *functionResult;
size_t functionResultLen;
fn = findFunction("one");
assert(fn != NULL);
memset(&gTestCommandState, 0, sizeof(gTestCommandState));
gTestCommandState.called = false;
gTestCommandState.returnValue = 25;
gTestCommandState.functionResult = "1234";
gTestCommandState.permissions = (PermissionRequestList *)1;
functionResult = NULL;
functionResultLen = 55;
ret = callFunction(fn, argc, argv,
&functionResult, &functionResultLen);
//xxx also try calling with a null resultLen arg (should succeed)
//xxx also try calling with a null argv element (should fail)
assert(ret == 25);
assert(gTestCommandState.called);
assert(strcmp(gTestCommandState.name, "one") == 0);
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();
return 0;
}
static int
test_interaction()
{
Command *cmd;
Function *fn;
int ret;
ret = commandInit();
assert(ret == 0);
/* Register some commands.
*/
ret = registerCommand("one", CMD_ARGS_WORDS, testCommand, (void *)0xc1);
assert(ret == 0);
ret = registerCommand("two", CMD_ARGS_WORDS, testCommand, (void *)0xc2);
assert(ret == 0);
/* Register some functions, one of which shares a name with a command.
*/
ret = registerFunction("one", testFunction, (void *)0xf1);
assert(ret == 0);
ret = registerFunction("three", testFunction, (void *)0xf3);
assert(ret == 0);
/* Look up each of the commands, and make sure no command exists
* with the name used only by our function.
*/
cmd = findCommand("one");
assert(cmd != NULL);
cmd = findCommand("two");
assert(cmd != NULL);
cmd = findCommand("three");
assert(cmd == NULL);
/* Look up each of the functions, and make sure no function exists
* with the name used only by our command.
*/
fn = findFunction("one");
assert(fn != NULL);
fn = findFunction("two");
assert(fn == NULL);
fn = findFunction("three");
assert(fn != NULL);
/* Set up some arguments.
*/
int argc = 4;
const char *argv[4] = { "ONE", "TWO", "THREE", "FOUR" };
/* Call the overlapping command and make sure that the cookie is correct.
*/
cmd = findCommand("one");
assert(cmd != NULL);
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);
assert(strcmp(gTestCommandState.name, "one") == 0);
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.
*/
char *functionResult;
size_t functionResultLen;
fn = findFunction("one");
assert(fn != NULL);
memset(&gTestCommandState, 0, sizeof(gTestCommandState));
gTestCommandState.called = false;
gTestCommandState.returnValue = 125;
gTestCommandState.functionResult = "5678";
gTestCommandState.permissions = (PermissionRequestList *)2;
functionResult = NULL;
functionResultLen = 66;
ret = callFunction(fn, argc, argv, &functionResult, &functionResultLen);
assert(ret == 125);
assert(gTestCommandState.called);
assert(strcmp(gTestCommandState.name, "one") == 0);
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));
/* Clean up.
*/
commandCleanup();
return 0;
}
int
test_cmd_fn()
{
int ret;
ret = test_commands();
if (ret != 0) {
fprintf(stderr, "test_commands() failed: %d\n", ret);
return ret;
}
ret = test_functions();
if (ret != 0) {
fprintf(stderr, "test_functions() failed: %d\n", ret);
return ret;
}
ret = test_interaction();
if (ret != 0) {
fprintf(stderr, "test_interaction() failed: %d\n", ret);
return ret;
}
return 0;
}

347
amend/test_permissions.c Normal file
View file

@ -0,0 +1,347 @@
/*
* 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;
}

146
amend/test_symtab.c Normal file
View file

@ -0,0 +1,146 @@
/*
* 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>
#undef NDEBUG
#include <assert.h>
#include "symtab.h"
int
test_symtab()
{
SymbolTable *tab;
void *cookie;
int ret;
/* Test creation */
tab = createSymbolTable();
assert(tab != NULL);
/* Smoke-test deletion */
deleteSymbolTable(tab);
tab = createSymbolTable();
assert(tab != NULL);
/* table parameter must be non-NULL. */
ret = addToSymbolTable(NULL, NULL, 0, NULL);
assert(ret < 0);
/* symbol parameter must be non-NULL. */
ret = addToSymbolTable(tab, NULL, 0, NULL);
assert(ret < 0);
/* cookie parameter must be non-NULL. */
ret = addToSymbolTable(tab, "null", 0, NULL);
assert(ret < 0);
/* table parameter must be non-NULL. */
cookie = findInSymbolTable(NULL, NULL, 0);
assert(cookie == NULL);
/* symbol parameter must be non-NULL. */
cookie = findInSymbolTable(tab, NULL, 0);
assert(cookie == NULL);
/* Try some actual inserts.
*/
ret = addToSymbolTable(tab, "one", 0, (void *)1);
assert(ret == 0);
ret = addToSymbolTable(tab, "two", 0, (void *)2);
assert(ret == 0);
ret = addToSymbolTable(tab, "three", 0, (void *)3);
assert(ret == 0);
/* Try some lookups.
*/
cookie = findInSymbolTable(tab, "one", 0);
assert((int)cookie == 1);
cookie = findInSymbolTable(tab, "two", 0);
assert((int)cookie == 2);
cookie = findInSymbolTable(tab, "three", 0);
assert((int)cookie == 3);
/* Try to insert something that's already there.
*/
ret = addToSymbolTable(tab, "one", 0, (void *)1111);
assert(ret < 0);
/* Make sure that the failed duplicate insert didn't
* clobber the original cookie value.
*/
cookie = findInSymbolTable(tab, "one", 0);
assert((int)cookie == 1);
/* Try looking up something that isn't there.
*/
cookie = findInSymbolTable(tab, "FOUR", 0);
assert(cookie == NULL);
/* Try looking up something that's similar to an existing entry.
*/
cookie = findInSymbolTable(tab, "on", 0);
assert(cookie == NULL);
cookie = findInSymbolTable(tab, "onee", 0);
assert(cookie == NULL);
/* Test flags.
* Try inserting something with a different flag.
*/
ret = addToSymbolTable(tab, "ten", 333, (void *)10);
assert(ret == 0);
/* Make sure it's there.
*/
cookie = findInSymbolTable(tab, "ten", 333);
assert((int)cookie == 10);
/* Make sure it's not there when looked up with a different flag.
*/
cookie = findInSymbolTable(tab, "ten", 0);
assert(cookie == NULL);
/* Try inserting something that has the same name as something
* with a different flag.
*/
ret = addToSymbolTable(tab, "one", 333, (void *)11);
assert(ret == 0);
/* Make sure the new entry exists.
*/
cookie = findInSymbolTable(tab, "one", 333);
assert((int)cookie == 11);
/* Make sure the old entry still has the right value.
*/
cookie = findInSymbolTable(tab, "one", 0);
assert((int)cookie == 1);
/* Try deleting again, now that there's stuff in the table.
*/
deleteSymbolTable(tab);
return 0;
}

View file

@ -0,0 +1 @@
I am a jelly donut.

View file

@ -0,0 +1,2 @@
This is a sample no-op test, which does at least serve to verify that the
test harness is working.

17
amend/tests/001-nop/run Normal file
View file

@ -0,0 +1,17 @@
#!/bin/bash
#
# 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.
echo 'I am a jelly donut.'

View file

View file

@ -0,0 +1 @@
EOF

View file

@ -0,0 +1 @@
Test to make sure that an empty file is accepted properly.

View file

View file

@ -0,0 +1,17 @@
#!/bin/bash
#
# 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.
amend --debug-lex input

View file

@ -0,0 +1,13 @@
IDENTIFIER<this_identifier_is_not_assert> EOL
IDENTIFIER<NEITHER_IS_THIS_123> EOL
IDENTIFIER<but_the_next_one_is> EOL
IDENTIFIER<assert> EOL
IDENTIFIER<next_one_is_not_an_identifier> EOL
line 6: unexpected character at '1'
EOF
line 1: unexpected character at '"'
EOF
line 1: unexpected character at '='
EOF
line 1: unexpected character at '9'
EOF

View file

@ -0,0 +1 @@
Test to make sure that simple command names are tokenized properly.

View file

@ -0,0 +1,6 @@
this_identifier_is_not_assert
NEITHER_IS_THIS_123
but_the_next_one_is
assert
next_one_is_not_an_identifier
12not_an_identifier

View file

@ -0,0 +1 @@
"quoted"

View file

@ -0,0 +1 @@
==

View file

@ -0,0 +1 @@
99

View file

@ -0,0 +1,20 @@
#!/bin/bash
#
# 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.
amend --debug-lex input
amend --debug-lex input2
amend --debug-lex input3
amend --debug-lex input4

View file

@ -0,0 +1,5 @@
IDENTIFIER<comment_on_this_line> EOL
IDENTIFIER<none_on_this_one> EOL
EOL
EOL
EOF

View file

@ -0,0 +1 @@
Test to make sure that comments are stripped out.

View file

@ -0,0 +1,4 @@
comment_on_this_line # this is a "comment" (with / a bunch) # \\ of stuff \
none_on_this_one
# beginning of line
# preceded by whitespace

View file

@ -0,0 +1,17 @@
#!/bin/bash
#
# 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.
amend --debug-lex input

View file

@ -0,0 +1,13 @@
IDENTIFIER<test> WORD<string> EOL
IDENTIFIER<test> WORD<string with spaces> EOL
IDENTIFIER<test> WORD<string with "escaped" quotes> EOL
IDENTIFIER<test> WORD<string with \escaped\ backslashes> EOL
IDENTIFIER<test> WORD<string with # a comment character> EOL
EOF
EOL
IDENTIFIER<test1>line 2: unterminated string at '
'
??? <0>
EOL
IDENTIFIER<test1>line 2: illegal escape at '\n'
??? <0>

View file

@ -0,0 +1 @@
Test to make sure that quoted strings are tokenized properly.

View file

@ -0,0 +1,5 @@
test "string"
test "string with spaces"
test "string with \"escaped\" quotes"
test "string with \\escaped\\ backslashes"
test "string with # a comment character"

View file

@ -0,0 +1,2 @@
# This should fail
test1 "unterminated string

View file

@ -0,0 +1,2 @@
# This should fail
test1 "string with illegal escape \n in the middle"

View file

@ -0,0 +1,19 @@
#!/bin/bash
#
# 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.
amend --debug-lex input
amend --debug-lex input2
amend --debug-lex input3

View file

View file

@ -0,0 +1,6 @@
IDENTIFIER<test> WORD<this> WORD<has> WORD<a> WORD<bunch> WORD<of> WORD<BARE> WORD<ALPHA> WORD<WORDS> EOL
IDENTIFIER<test> WORD<12> WORD<this> WORD<has(some> WORD<)> WORD<ALPHANUMER1C> WORD<and> WORD<\\> WORD<whatever> WORD<characters> EOL
IDENTIFIER<test> WORD<this> WORD<has> WORD<mixed> WORD<bare> WORD<and quoted> WORD<words> EOL
IDENTIFIER<test> WORD<what> WORD<about> WORD<quotesin the middle?> EOL
IDENTIFIER<test> WORD<"""shouldn't> WORD<be> WORD<a> WORD<quoted> WORD<string> EOL
EOF

View file

@ -0,0 +1 @@
Test to make sure that argument words are tokenized properly.

View file

@ -0,0 +1,5 @@
test this has a bunch of BARE ALPHA WORDS
test 12 this has(some ) ALPHANUMER1C and \\ whatever characters
test this has mixed bare "and quoted" words
test what about quotes"in the middle?"
test \"\"\"shouldn't be a quoted string

View file

@ -0,0 +1,2 @@
# This should fail
test1 "unterminated string

View file

@ -0,0 +1,2 @@
# This should fail
test1 "string with illegal escape \n in the middle"

View file

@ -0,0 +1,17 @@
#!/bin/bash
#
# 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.
amend --debug-lex input

View file

@ -0,0 +1,11 @@
IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<112345oldhashvalue1234123> EOL
IDENTIFIER<mark> WORD<SYS:> WORD<dirty> EOL
IDENTIFIER<copy_dir> WORD<PKG:android-files> WORD<SYS:> EOL
IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<667890newhashvalue6678909> EOL
IDENTIFIER<mark> WORD<SYS:> WORD<clean> EOL
IDENTIFIER<done> EOL
IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> , STRING<blah> ) == STRING<112345oldhashvalue1234123> EOL
IDENTIFIER<assert> STRING<true> == STRING<false> EOL
IDENTIFIER<assert> IDENTIFIER<one> ( STRING<abc> , IDENTIFIER<two> ( STRING<def> ) ) == STRING<five> EOL
IDENTIFIER<assert> IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<667890newhashvalue6678909> || IDENTIFIER<hash_dir> ( STRING<SYS:> ) == STRING<667890newhashvalue6678909> EOL
EOF

View file

@ -0,0 +1 @@
An input script similar to one that will actually be used in practice.

View file

@ -0,0 +1,10 @@
assert hash_dir("SYS:") == "112345oldhashvalue1234123"
mark SYS: dirty
copy_dir "PKG:android-files" SYS:
assert hash_dir("SYS:") == "667890newhashvalue6678909"
mark SYS: clean
done
assert hash_dir("SYS:", "blah") == "112345oldhashvalue1234123"
assert "true" == "false"
assert one("abc", two("def")) == "five"
assert hash_dir("SYS:") == "667890newhashvalue6678909" || hash_dir("SYS:") == "667890newhashvalue6678909"

View file

@ -0,0 +1,17 @@
#!/bin/bash
#
# 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.
amend --debug-lex input

View file

@ -0,0 +1,74 @@
command "assert" {
STRING EQ {
FUNCTION hash_dir (
"SYS:"
)
"112345oldhashvalue1234123"
}
}
command "mark" {
"SYS:"
"dirty"
}
command "copy_dir" {
"PKG:android-files"
"SYS:"
}
command "assert" {
STRING EQ {
FUNCTION hash_dir (
"SYS:"
)
"667890newhashvalue6678909"
}
}
command "mark" {
"SYS:"
"clean"
}
command "done" {
}
command "assert" {
STRING EQ {
FUNCTION hash_dir (
"SYS:"
"blah"
)
"112345oldhashvalue1234123"
}
}
command "assert" {
STRING EQ {
"true"
"false"
}
}
command "assert" {
STRING NE {
FUNCTION matches (
FUNCTION hash_dir (
"SYS:"
)
"667890newhashvalue6678909"
"999999newhashvalue6678909"
)
""
}
}
command "assert" {
BOOLEAN OR {
STRING EQ {
FUNCTION hash_dir (
"SYS:"
)
"667890newhashvalue6678909"
}
STRING EQ {
FUNCTION hash_dir (
"SYS:"
)
"999999newhashvalue6678909"
}
}
}
amend: Parse successful.

View file

@ -0,0 +1 @@
An input script similar to one that will actually be used in practice.

View file

@ -0,0 +1,10 @@
assert hash_dir("SYS:") == "112345oldhashvalue1234123"
mark SYS: dirty
copy_dir "PKG:android-files" SYS:
assert hash_dir("SYS:") == "667890newhashvalue6678909"
mark SYS: clean
done
assert hash_dir("SYS:", "blah") == "112345oldhashvalue1234123"
assert "true" == "false"
assert matches(hash_dir("SYS:"), "667890newhashvalue6678909", "999999newhashvalue6678909") != ""
assert hash_dir("SYS:") == "667890newhashvalue6678909" || hash_dir("SYS:") == "999999newhashvalue6678909"

View file

@ -0,0 +1,17 @@
#!/bin/bash
#
# 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.
amend --debug-ast input

View file

View file

150
amend/tests/one-test Executable file
View file

@ -0,0 +1,150 @@
#!/bin/bash
#
# 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.
# Set up prog to be the path of this script, including following symlinks,
# and set up progdir to be the fully-qualified pathname of its directory.
prog="$0"
while [ -h "${prog}" ]; do
newProg=`/bin/ls -ld "${prog}"`
newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
if expr "x${newProg}" : 'x/' >/dev/null; then
prog="${newProg}"
else
progdir=`dirname "${prog}"`
prog="${progdir}/${newProg}"
fi
done
oldwd=`pwd`
progdir=`dirname "${prog}"`
cd "${progdir}"
progdir=`pwd`
prog="${progdir}"/`basename "${prog}"`
info="info.txt"
run="run"
expected="expected.txt"
output="out.txt"
skip="SKIP"
dev_mode="no"
if [ "x$1" = "x--dev" ]; then
dev_mode="yes"
shift
fi
update_mode="no"
if [ "x$1" = "x--update" ]; then
update_mode="yes"
shift
fi
usage="no"
if [ "x$1" = "x--help" ]; then
usage="yes"
else
if [ "x$1" = "x" ]; then
testdir=`basename "$oldwd"`
else
testdir="$1"
fi
if [ '!' -d "$testdir" ]; then
td2=`echo ${testdir}-*`
if [ '!' -d "$td2" ]; then
echo "${testdir}: no such test directory" 1>&2
usage="yes"
fi
testdir="$td2"
fi
fi
if [ "$usage" = "yes" ]; then
prog=`basename $prog`
(
echo "usage:"
echo " $prog --help Print this message."
echo " $prog testname Run test normally."
echo " $prog --dev testname Development mode (dump to stdout)."
echo " $prog --update testname Update mode (replace expected.txt)."
echo " Omitting the test name uses the current directory as the test."
) 1>&2
exit 1
fi
td_info="$testdir"/"$info"
td_run="$testdir"/"$run"
td_expected="$testdir"/"$expected"
td_skip="$testdir"/"$skip"
if [ -r "$td_skip" ]; then
exit 2
fi
tmpdir=/tmp/test-$$
if [ '!' '(' -r "$td_info" -a -r "$td_run" -a -r "$td_expected" ')' ]; then
echo "${testdir}: missing files" 1>&2
exit 1
fi
# copy the test to a temp dir and run it
echo "${testdir}: running..." 1>&2
rm -rf "$tmpdir"
cp -Rp "$testdir" "$tmpdir"
cd "$tmpdir"
chmod 755 "$run"
#PATH="${progdir}/../build/bin:${PATH}"
good="no"
if [ "$dev_mode" = "yes" ]; then
"./$run" 2>&1
echo "exit status: $?" 1>&2
good="yes"
elif [ "$update_mode" = "yes" ]; then
"./$run" >"${progdir}/$td_expected" 2>&1
good="yes"
else
"./$run" >"$output" 2>&1
cmp -s "$expected" "$output"
if [ "$?" = "0" ]; then
# output == expected
good="yes"
echo "$testdir"': succeeded!' 1>&2
fi
fi
if [ "$good" = "yes" ]; then
cd "$oldwd"
rm -rf "$tmpdir"
exit 0
fi
(
echo "${testdir}: FAILED!"
echo ' '
echo '#################### info'
cat "$info" | sed 's/^/# /g'
echo '#################### diffs'
diff -u "$expected" "$output"
echo '####################'
echo ' '
echo "files left in $tmpdir"
) 1>&2
exit 1

69
amend/tests/run-all-tests Executable file
View file

@ -0,0 +1,69 @@
#!/bin/bash
#
# 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.
# Set up prog to be the path of this script, including following symlinks,
# and set up progdir to be the fully-qualified pathname of its directory.
prog="$0"
while [ -h "${prog}" ]; do
newProg=`/bin/ls -ld "${prog}"`
newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
if expr "x${newProg}" : 'x/' >/dev/null; then
prog="${newProg}"
else
progdir=`dirname "${prog}"`
prog="${progdir}/${newProg}"
fi
done
oldwd=`pwd`
progdir=`dirname "${prog}"`
cd "${progdir}"
progdir=`pwd`
prog="${progdir}"/`basename "${prog}"`
passed=0
skipped=0
skipNames=""
failed=0
failNames=""
for i in *; do
if [ -d "$i" -a -r "$i" ]; then
./one-test "$i"
status=$?
if [ "$status" = "0" ]; then
((passed += 1))
elif [ "$status" = "2" ]; then
((skipped += 1))
skipNames="$skipNames $i"
else
((failed += 1))
failNames="$failNames $i"
fi
fi
done
echo "passed: $passed test(s)"
echo "skipped: $skipped test(s)"
for i in $skipNames; do
echo "skipped: $i"
done
echo "failed: $failed test(s)"
for i in $failNames; do
echo "failed: $i"
done

267
bootloader.c Normal file
View file

@ -0,0 +1,267 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "bootloader.h"
#include "common.h"
#include "mtdutils/mtdutils.h"
#include "roots.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
static const char *CACHE_NAME = "CACHE:";
static const char *MISC_NAME = "MISC:";
static const int MISC_PAGES = 3; // number of pages to save
static const int MISC_COMMAND_PAGE = 1; // bootloader command is this page
#ifdef LOG_VERBOSE
static void dump_data(const char *data, int len) {
int pos;
for (pos = 0; pos < len; ) {
printf("%05x: %02x", pos, data[pos]);
for (++pos; pos < len && (pos % 24) != 0; ++pos) {
printf(" %02x", data[pos]);
}
printf("\n");
}
}
#endif
int get_bootloader_message(struct bootloader_message *out) {
size_t write_size;
const MtdPartition *part = get_root_mtd_partition(MISC_NAME);
if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
LOGE("Can't find %s\n", MISC_NAME);
return -1;
}
MtdReadContext *read = mtd_read_partition(part);
if (read == NULL) {
LOGE("Can't open %s\n(%s)\n", MISC_NAME, strerror(errno));
return -1;
}
const ssize_t size = write_size * MISC_PAGES;
char data[size];
ssize_t r = mtd_read_data(read, data, size);
if (r != size) LOGE("Can't read %s\n(%s)\n", MISC_NAME, strerror(errno));
mtd_read_close(read);
if (r != size) return -1;
#ifdef LOG_VERBOSE
printf("\n--- get_bootloader_message ---\n");
dump_data(data, size);
printf("\n");
#endif
memcpy(out, &data[write_size * MISC_COMMAND_PAGE], sizeof(*out));
return 0;
}
int set_bootloader_message(const struct bootloader_message *in) {
size_t write_size;
const MtdPartition *part = get_root_mtd_partition(MISC_NAME);
if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
LOGE("Can't find %s\n", MISC_NAME);
return -1;
}
MtdReadContext *read = mtd_read_partition(part);
if (read == NULL) {
LOGE("Can't open %s\n(%s)\n", MISC_NAME, strerror(errno));
return -1;
}
ssize_t size = write_size * MISC_PAGES;
char data[size];
ssize_t r = mtd_read_data(read, data, size);
if (r != size) LOGE("Can't read %s\n(%s)\n", MISC_NAME, strerror(errno));
mtd_read_close(read);
if (r != size) return -1;
memcpy(&data[write_size * MISC_COMMAND_PAGE], in, sizeof(*in));
#ifdef LOG_VERBOSE
printf("\n--- set_bootloader_message ---\n");
dump_data(data, size);
printf("\n");
#endif
MtdWriteContext *write = mtd_write_partition(part);
if (write == NULL) {
LOGE("Can't open %s\n(%s)\n", MISC_NAME, strerror(errno));
return -1;
}
if (mtd_write_data(write, data, size) != size) {
LOGE("Can't write %s\n(%s)\n", MISC_NAME, strerror(errno));
mtd_write_close(write);
return -1;
}
if (mtd_write_close(write)) {
LOGE("Can't finish %s\n(%s)\n", MISC_NAME, strerror(errno));
return -1;
}
LOGI("Set boot command \"%s\"\n", in->command[0] != 255 ? in->command : "");
return 0;
}
/* Update Image
*
* - will be stored in the "cache" partition
* - bad blocks will be ignored, like boot.img and recovery.img
* - the first block will be the image header (described below)
* - the size is in BYTES, inclusive of the header
* - offsets are in BYTES from the start of the update header
* - two raw bitmaps will be included, the "busy" and "fail" bitmaps
* - for dream, the bitmaps will be 320x480x16bpp RGB565
*/
#define UPDATE_MAGIC "MSM-RADIO-UPDATE"
#define UPDATE_MAGIC_SIZE 16
#define UPDATE_VERSION 0x00010000
struct update_header {
unsigned char MAGIC[UPDATE_MAGIC_SIZE];
unsigned version;
unsigned size;
unsigned image_offset;
unsigned image_length;
unsigned bitmap_width;
unsigned bitmap_height;
unsigned bitmap_bpp;
unsigned busy_bitmap_offset;
unsigned busy_bitmap_length;
unsigned fail_bitmap_offset;
unsigned fail_bitmap_length;
};
int write_update_for_bootloader(
const char *update, int update_length,
int bitmap_width, int bitmap_height, int bitmap_bpp,
const char *busy_bitmap, const char *fail_bitmap) {
if (ensure_root_path_unmounted(CACHE_NAME)) {
LOGE("Can't unmount %s\n", CACHE_NAME);
return -1;
}
const MtdPartition *part = get_root_mtd_partition(CACHE_NAME);
if (part == NULL) {
LOGE("Can't find %s\n", CACHE_NAME);
return -1;
}
MtdWriteContext *write = mtd_write_partition(part);
if (write == NULL) {
LOGE("Can't open %s\n(%s)\n", CACHE_NAME, strerror(errno));
return -1;
}
/* Write an invalid (zero) header first, to disable any previous
* update and any other structured contents (like a filesystem),
* and as a placeholder for the amount of space required.
*/
struct update_header header;
memset(&header, 0, sizeof(header));
const ssize_t header_size = sizeof(header);
if (mtd_write_data(write, (char*) &header, header_size) != header_size) {
LOGE("Can't write header to %s\n(%s)\n", CACHE_NAME, strerror(errno));
mtd_write_close(write);
return -1;
}
/* Write each section individually block-aligned, so we can write
* each block independently without complicated buffering.
*/
memcpy(&header.MAGIC, UPDATE_MAGIC, UPDATE_MAGIC_SIZE);
header.version = UPDATE_VERSION;
header.size = header_size;
header.image_offset = mtd_erase_blocks(write, 0);
header.image_length = update_length;
if ((int) header.image_offset == -1 ||
mtd_write_data(write, update, update_length) != update_length) {
LOGE("Can't write update to %s\n(%s)\n", CACHE_NAME, strerror(errno));
mtd_write_close(write);
return -1;
}
header.bitmap_width = bitmap_width;
header.bitmap_height = bitmap_height;
header.bitmap_bpp = bitmap_bpp;
int bitmap_length = (bitmap_bpp + 7) / 8 * bitmap_width * bitmap_height;
header.busy_bitmap_offset = mtd_erase_blocks(write, 0);
header.busy_bitmap_length = busy_bitmap != NULL ? bitmap_length : 0;
if ((int) header.busy_bitmap_offset == -1 ||
mtd_write_data(write, busy_bitmap, bitmap_length) != bitmap_length) {
LOGE("Can't write bitmap to %s\n(%s)\n", CACHE_NAME, strerror(errno));
mtd_write_close(write);
return -1;
}
header.fail_bitmap_offset = mtd_erase_blocks(write, 0);
header.fail_bitmap_length = fail_bitmap != NULL ? bitmap_length : 0;
if ((int) header.fail_bitmap_offset == -1 ||
mtd_write_data(write, fail_bitmap, bitmap_length) != bitmap_length) {
LOGE("Can't write bitmap to %s\n(%s)\n", CACHE_NAME, strerror(errno));
mtd_write_close(write);
return -1;
}
/* Write the header last, after all the blocks it refers to, so that
* when the magic number is installed everything is valid.
*/
if (mtd_write_close(write)) {
LOGE("Can't finish writing %s\n(%s)\n", CACHE_NAME, strerror(errno));
return -1;
}
write = mtd_write_partition(part);
if (write == NULL) {
LOGE("Can't reopen %s\n(%s)\n", CACHE_NAME, strerror(errno));
return -1;
}
if (mtd_write_data(write, (char*) &header, header_size) != header_size) {
LOGE("Can't rewrite header to %s\n(%s)\n", CACHE_NAME, strerror(errno));
mtd_write_close(write);
return -1;
}
if (mtd_erase_blocks(write, 0) != (off_t) header.image_offset) {
LOGE("Misalignment rewriting %s\n(%s)\n", CACHE_NAME, strerror(errno));
mtd_write_close(write);
return -1;
}
if (mtd_write_close(write)) {
LOGE("Can't finish header of %s\n(%s)\n", CACHE_NAME, strerror(errno));
return -1;
}
return 0;
}

59
bootloader.h Normal file
View file

@ -0,0 +1,59 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _RECOVERY_BOOTLOADER_H
#define _RECOVERY_BOOTLOADER_H
/* Bootloader Message
*
* This structure describes the content of a block in flash
* that is used for recovery and the bootloader to talk to
* each other.
*
* The command field is updated by linux when it wants to
* reboot into recovery or to update radio or bootloader firmware.
* It is also updated by the bootloader when firmware update
* is complete (to boot into recovery for any final cleanup)
*
* The status field is written by the bootloader after the
* completion of an "update-radio" or "update-hboot" command.
*
* The recovery field is only written by linux and used
* for the system to send a message to recovery or the
* other way around.
*/
struct bootloader_message {
char command[32];
char status[32];
char recovery[1024];
};
/* Read and write the bootloader command from the "misc" partition.
* These return zero on success.
*/
int get_bootloader_message(struct bootloader_message *out);
int set_bootloader_message(const struct bootloader_message *in);
/* Write an update to the cache partition for update-radio or update-hboot.
* Note, this destroys any filesystem on the cache partition!
* The expected bitmap format is 240x320, 16bpp (2Bpp), RGB 5:6:5.
*/
int write_update_for_bootloader(
const char *update, int update_len,
int bitmap_width, int bitmap_height, int bitmap_bpp,
const char *busy_bitmap, const char *error_bitmap);
#endif

1148
commands.c Normal file

File diff suppressed because it is too large Load diff

28
commands.h Normal file
View file

@ -0,0 +1,28 @@
/*
* 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 RECOVERY_COMMANDS_H_
#define RECOVERY_COMMANDS_H_
#include "minzip/Zip.h"
typedef struct {
ZipArchive *package;
} RecoveryCommandContext;
int register_update_commands(RecoveryCommandContext *ctx);
#endif // RECOVERY_COMMANDS_H_

94
common.h Normal file
View file

@ -0,0 +1,94 @@
/*
* 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 RECOVERY_COMMON_H
#define RECOVERY_COMMON_H
#include <stdio.h>
// Initialize the graphics system.
void ui_init();
// Use KEY_* codes from <linux/input.h> or KEY_DREAM_* from "minui/minui.h".
int ui_wait_key(); // waits for a key/button press, returns the code
int ui_key_pressed(int key); // returns >0 if the code is currently pressed
int ui_text_visible(); // returns >0 if text log is currently visible
void ui_clear_key_queue();
// Write a message to the on-screen log shown with Alt-L (also to stderr).
// The screen is small, and users may need to report these messages to support,
// so keep the output short and not too cryptic.
void ui_print(const char *fmt, ...);
// Display some header text followed by a menu of items, which appears
// at the top of the screen (in place of any scrolling ui_print()
// output, if necessary).
void ui_start_menu(char** headers, char** items);
// Set the menu highlight to the given index, and return it (capped to
// the range [0..numitems).
int ui_menu_select(int sel);
// End menu mode, resetting the text overlay so that ui_print()
// statements will be displayed.
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,
BACKGROUND_ICON_FIRMWARE_ERROR,
NUM_BACKGROUND_ICONS
};
void ui_set_background(int icon);
// Get a malloc'd copy of the screen image showing (only) the specified icon.
// Also returns the width, height, and bits per pixel of the returned image.
// TODO: Use some sort of "struct Bitmap" here instead of all these variables?
char *ui_copy_image(int icon, int *width, int *height, int *bpp);
// Show a progress bar and define the scope of the next operation:
// portion - fraction of the progress bar the next operation will use
// seconds - expected time interval (progress bar moves at this minimum rate)
void ui_show_progress(float portion, int seconds);
void ui_set_progress(float fraction); // 0.0 - 1.0 within the defined scope
// Default allocation of progress bar segments to operations
static const int VERIFICATION_PROGRESS_TIME = 60;
static const float VERIFICATION_PROGRESS_FRACTION = 0.5;
static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4;
static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1;
// Show a rotating "barberpole" for ongoing operations. Updates automatically.
void ui_show_indeterminate_progress();
// Hide and reset the progress bar.
void ui_reset_progress();
#define LOGE(...) ui_print("E:" __VA_ARGS__)
#define LOGW(...) fprintf(stderr, "W:" __VA_ARGS__)
#define LOGI(...) fprintf(stderr, "I:" __VA_ARGS__)
#if 0
#define LOGV(...) fprintf(stderr, "V:" __VA_ARGS__)
#define LOGD(...) fprintf(stderr, "D:" __VA_ARGS__)
#else
#define LOGV(...) do {} while (0)
#define LOGD(...) do {} while (0)
#endif
#endif // RECOVERY_COMMON_H

View file

@ -0,0 +1,8 @@
assert compatible_with("0.1") == "true"
assert file_contains("SYSTEM:build.prop", "ro.product.device=dream") == "true" || file_contains("SYSTEM:build.prop", "ro.build.product=dream") == "true"
assert file_contains("RECOVERY:default.prop", "ro.product.device=dream") == "true" || file_contains("RECOVERY:default.prop", "ro.build.product=dream") == "true"
assert getprop("ro.product.device") == "dream"
format BOOT:
format SYSTEM:
copy_dir PACKAGE:system SYSTEM:
write_raw_image PACKAGE:boot.img BOOT:

33
etc/init.rc Normal file
View file

@ -0,0 +1,33 @@
on init
export PATH /sbin
export ANDROID_ROOT /system
export ANDROID_DATA /data
export EXTERNAL_STORAGE /sdcard
symlink /system/etc /etc
mkdir /sdcard
mkdir /system
mkdir /data
mkdir /cache
mount /tmp /tmp tmpfs
on boot
ifup lo
hostname localhost
domainname localdomain
class_start default
service recovery /sbin/recovery
service adbd /sbin/adbd recovery
on property:persist.service.adb.enable=1
start adbd
on property:persist.service.adb.enable=0
stop adbd

127
firmware.c Normal file
View file

@ -0,0 +1,127 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "bootloader.h"
#include "common.h"
#include "firmware.h"
#include "roots.h"
#include <errno.h>
#include <string.h>
#include <sys/reboot.h>
static const char *update_type = NULL;
static const char *update_data = NULL;
static int update_length = 0;
int remember_firmware_update(const char *type, const char *data, int length) {
if (update_type != NULL || update_data != NULL) {
LOGE("Multiple firmware images\n");
return -1;
}
update_type = type;
update_data = data;
update_length = length;
return 0;
}
/* Bootloader / Recovery Flow
*
* On every boot, the bootloader will read the bootloader_message
* from flash and check the command field. The bootloader should
* deal with the command field not having a 0 terminator correctly
* (so as to not crash if the block is invalid or corrupt).
*
* The bootloader will have to publish the partition that contains
* the bootloader_message to the linux kernel so it can update it.
*
* if command == "boot-recovery" -> boot recovery.img
* else if command == "update-radio" -> update radio image (below)
* else if command == "update-hboot" -> update hboot image (below)
* else -> boot boot.img (normal boot)
*
* Radio/Hboot Update Flow
* 1. the bootloader will attempt to load and validate the header
* 2. if the header is invalid, status="invalid-update", goto #8
* 3. display the busy image on-screen
* 4. if the update image is invalid, status="invalid-radio-image", goto #8
* 5. attempt to update the firmware (depending on the command)
* 6. if successful, status="okay", goto #8
* 7. if failed, and the old image can still boot, status="failed-update"
* 8. write the bootloader_message, leaving the recovery field
* unchanged, updating status, and setting command to
* "boot-recovery"
* 9. reboot
*
* The bootloader will not modify or erase the cache partition.
* It is recovery's responsibility to clean up the mess afterwards.
*/
int maybe_install_firmware_update(const char *send_intent) {
if (update_data == NULL || update_length == 0) return 0;
/* We destroy the cache partition to pass the update image to the
* bootloader, so all we can really do afterwards is wipe cache and reboot.
* Set up this instruction now, in case we're interrupted while writing.
*/
struct bootloader_message boot;
memset(&boot, 0, sizeof(boot));
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
strlcpy(boot.recovery, "recovery\n--wipe_cache\n", sizeof(boot.command));
if (send_intent != NULL) {
strlcat(boot.recovery, "--send_intent=", sizeof(boot.recovery));
strlcat(boot.recovery, send_intent, sizeof(boot.recovery));
strlcat(boot.recovery, "\n", sizeof(boot.recovery));
}
if (set_bootloader_message(&boot)) return -1;
int width = 0, height = 0, bpp = 0;
char *busy_image = ui_copy_image(
BACKGROUND_ICON_FIRMWARE_INSTALLING, &width, &height, &bpp);
char *fail_image = ui_copy_image(
BACKGROUND_ICON_FIRMWARE_ERROR, &width, &height, &bpp);
ui_print("Writing %s image...\n", update_type);
if (write_update_for_bootloader(
update_data, update_length,
width, height, bpp, busy_image, fail_image)) {
LOGE("Can't write %s image\n(%s)\n", update_type, strerror(errno));
format_root_device("CACHE:"); // Attempt to clean cache up, at least.
return -1;
}
free(busy_image);
free(fail_image);
/* The update image is fully written, so now we can instruct the bootloader
* to install it. (After doing so, it will come back here, and we will
* wipe the cache and reboot into the system.)
*/
snprintf(boot.command, sizeof(boot.command), "update-%s", update_type);
if (set_bootloader_message(&boot)) {
format_root_device("CACHE:");
return -1;
}
reboot(RB_AUTOBOOT);
// Can't reboot? WTF?
LOGE("Can't reboot\n");
return -1;
}

32
firmware.h Normal file
View file

@ -0,0 +1,32 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _RECOVERY_FIRMWARE_H
#define _RECOVERY_FIRMWARE_H
/* Save a radio or bootloader update image for later installation.
* The type should be one of "hboot" or "radio".
* Takes ownership of type and data. Returns nonzero on error.
*/
int remember_firmware_update(const char *type, const char *data, int length);
/* 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...
*/
int maybe_install_firmware_update(const char *send_intent);
#endif

186
install.c Normal file
View file

@ -0,0 +1,186 @@
/*
* 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 <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/stat.h>
#include "amend/amend.h"
#include "common.h"
#include "install.h"
#include "mincrypt/rsa.h"
#include "minui/minui.h"
#include "minzip/SysUtil.h"
#include "minzip/Zip.h"
#include "mtdutils/mounts.h"
#include "mtdutils/mtdutils.h"
#include "roots.h"
#include "verifier.h"
/* List of public keys */
static const RSAPublicKey keys[] = {
#include "keys.inc"
};
#define ASSUMED_UPDATE_SCRIPT_NAME "META-INF/com/google/android/update-script"
static const ZipEntry *
find_update_script(ZipArchive *zip)
{
//TODO: Get the location of this script from the MANIFEST.MF file
return mzFindZipEntry(zip, ASSUMED_UPDATE_SCRIPT_NAME);
}
static int read_data(ZipArchive *zip, const ZipEntry *entry,
char** ppData, int* pLength) {
int len = (int)mzGetZipEntryUncompLen(entry);
if (len <= 0) {
LOGE("Bad data length %d\n", len);
return -1;
}
char *data = malloc(len + 1);
if (data == NULL) {
LOGE("Can't allocate %d bytes for data\n", len + 1);
return -2;
}
bool ok = mzReadZipEntry(zip, entry, data, len);
if (!ok) {
LOGE("Error while reading data\n");
free(data);
return -3;
}
data[len] = '\0'; // not necessary, but just to be safe
*ppData = data;
if (pLength) {
*pLength = len;
}
return 0;
}
static int
handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry)
{
/* Read the entire script into a buffer.
*/
int script_len;
char* script_data;
if (read_data(zip, update_script_entry, &script_data, &script_len) < 0) {
LOGE("Can't read update script\n");
return INSTALL_ERROR;
}
/* Parse the script. Note that the script and parse tree are never freed.
*/
const AmCommandList *commands = parseAmendScript(script_data, script_len);
if (commands == NULL) {
LOGE("Syntax error in update script\n");
return INSTALL_ERROR;
} else {
UnterminatedString name = mzGetZipEntryFileName(update_script_entry);
LOGI("Parsed %.*s\n", name.len, name.str);
}
/* Execute the script.
*/
int ret = execCommandList((ExecContext *)1, commands);
if (ret != 0) {
int num = ret;
char *line, *next = script_data;
while (next != NULL && ret-- > 0) {
line = next;
next = memchr(line, '\n', script_data + script_len - line);
if (next != NULL) *next++ = '\0';
}
LOGE("Failure at line %d:\n%s\n", num, next ? line : "(not found)");
return INSTALL_ERROR;
}
ui_print("Installation complete.\n");
return INSTALL_SUCCESS;
}
static int
handle_update_package(const char *path, ZipArchive *zip)
{
// Give verification half the progress bar...
ui_print("Verifying update package...\n");
ui_show_progress(
VERIFICATION_PROGRESS_FRACTION,
VERIFICATION_PROGRESS_TIME);
if (!verify_jar_signature(zip, keys, sizeof(keys) / sizeof(keys[0]))) {
LOGE("Verification failed\n");
return INSTALL_CORRUPT;
}
// Update should take the rest of the progress bar.
ui_print("Installing update...\n");
const ZipEntry *script_entry;
script_entry = find_update_script(zip);
if (script_entry == NULL) {
LOGE("Can't find update script\n");
return INSTALL_CORRUPT;
}
if (register_package_root(zip, path) < 0) {
LOGE("Can't register package root\n");
return INSTALL_ERROR;
}
int ret = handle_update_script(zip, script_entry);
register_package_root(NULL, NULL); // Unregister package root
return ret;
}
int
install_package(const char *root_path)
{
ui_set_background(BACKGROUND_ICON_INSTALLING);
ui_print("Finding update package...\n");
ui_show_indeterminate_progress();
LOGI("Update location: %s\n", root_path);
if (ensure_root_path_mounted(root_path) != 0) {
LOGE("Can't mount %s\n", root_path);
return INSTALL_CORRUPT;
}
char path[PATH_MAX] = "";
if (translate_root_path(root_path, path, sizeof(path)) == NULL) {
LOGE("Bad path %s\n", root_path);
return INSTALL_CORRUPT;
}
ui_print("Opening update package...\n");
LOGI("Update file path: %s\n", path);
/* Try to open the package.
*/
ZipArchive zip;
int err = mzOpenZipArchive(path, &zip);
if (err != 0) {
LOGE("Can't open %s\n(%s)\n", path, err != -1 ? strerror(err) : "bad");
return INSTALL_CORRUPT;
}
/* Verify and install the contents of the package.
*/
int status = handle_update_package(path, &zip);
mzCloseZipArchive(&zip);
return status;
}

25
install.h Normal file
View file

@ -0,0 +1,25 @@
/*
* 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 RECOVERY_INSTALL_H_
#define RECOVERY_INSTALL_H_
#include "common.h"
enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT };
int install_package(const char *root_path);
#endif // RECOVERY_INSTALL_H_

12
minui/Android.mk Normal file
View file

@ -0,0 +1,12 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := graphics.c events.c resources.c
LOCAL_C_INCLUDES +=\
external/libpng\
external/zlib
LOCAL_MODULE := libminui
include $(BUILD_STATIC_LIBRARY)

82
minui/events.c Normal file
View file

@ -0,0 +1,82 @@
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/poll.h>
#include <linux/input.h>
#include "minui.h"
#define MAX_DEVICES 16
static struct pollfd ev_fds[MAX_DEVICES];
static unsigned ev_count = 0;
int ev_init(void)
{
DIR *dir;
struct dirent *de;
int fd;
dir = opendir("/dev/input");
if(dir != 0) {
while((de = readdir(dir))) {
// fprintf(stderr,"/dev/input/%s\n", de->d_name);
if(strncmp(de->d_name,"event",5)) continue;
fd = openat(dirfd(dir), de->d_name, O_RDONLY);
if(fd < 0) continue;
ev_fds[ev_count].fd = fd;
ev_fds[ev_count].events = POLLIN;
ev_count++;
if(ev_count == MAX_DEVICES) break;
}
}
return 0;
}
void ev_exit(void)
{
while (ev_count > 0) {
close(ev_fds[--ev_count].fd);
}
}
int ev_get(struct input_event *ev, unsigned dont_wait)
{
int r;
unsigned n;
do {
r = poll(ev_fds, ev_count, dont_wait ? 0 : -1);
if(r > 0) {
for(n = 0; n < ev_count; n++) {
if(ev_fds[n].revents & POLLIN) {
r = read(ev_fds[n].fd, ev, sizeof(*ev));
if(r == sizeof(*ev)) return 0;
}
}
}
} while(dont_wait == 0);
return -1;
}

214
minui/font_10x18.h Normal file
View file

@ -0,0 +1,214 @@
struct {
unsigned width;
unsigned height;
unsigned cwidth;
unsigned cheight;
unsigned char rundata[];
} font = {
.width = 960,
.height = 18,
.cwidth = 10,
.cheight = 18,
.rundata = {
0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x55,0x82,0x06,0x82,0x02,0x82,0x10,0x82,
0x11,0x83,0x08,0x82,0x0a,0x82,0x04,0x82,0x46,0x82,0x08,0x82,0x07,0x84,0x06,
0x84,0x0a,0x81,0x03,0x88,0x04,0x84,0x04,0x88,0x04,0x84,0x06,0x84,0x1e,0x81,
0x0e,0x81,0x0a,0x84,0x06,0x84,0x07,0x82,0x05,0x85,0x07,0x84,0x04,0x86,0x04,
0x88,0x02,0x88,0x04,0x84,0x04,0x82,0x04,0x82,0x02,0x88,0x05,0x86,0x01,0x82,
0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x04,0x84,0x04,
0x86,0x06,0x84,0x04,0x86,0x06,0x84,0x04,0x88,0x02,0x82,0x04,0x82,0x02,0x82,
0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,
0x88,0x03,0x86,0x0e,0x86,0x06,0x82,0x11,0x82,0x10,0x82,0x18,0x82,0x0f,0x84,
0x0d,0x82,0x1c,0x82,0x09,0x84,0x7f,0x16,0x84,0x05,0x82,0x05,0x84,0x07,0x83,
0x02,0x82,0x19,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x01,0x82,0x03,0x86,0x04,
0x83,0x02,0x82,0x03,0x82,0x01,0x82,0x07,0x82,0x09,0x82,0x06,0x82,0x3e,0x82,
0x04,0x84,0x06,0x83,0x06,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x03,
0x82,0x09,0x82,0x02,0x82,0x09,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,
0x1c,0x82,0x0e,0x82,0x08,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x05,0x84,0x04,
0x82,0x02,0x82,0x05,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x82,
0x09,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x04,
0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x03,0x82,0x02,0x82,
0x03,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x02,
0x82,0x06,0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,
0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x03,0x82,0x08,0x82,0x0c,
0x82,0x05,0x84,0x11,0x82,0x0f,0x82,0x18,0x82,0x0e,0x82,0x02,0x82,0x0c,0x82,
0x1c,0x82,0x0b,0x82,0x7f,0x15,0x82,0x08,0x82,0x08,0x82,0x05,0x82,0x01,0x82,
0x01,0x82,0x19,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x01,0x82,0x02,0x82,0x01,
0x82,0x01,0x82,0x02,0x82,0x01,0x82,0x01,0x82,0x03,0x82,0x01,0x82,0x07,0x82,
0x08,0x82,0x08,0x82,0x3d,0x82,0x03,0x82,0x02,0x82,0x04,0x84,0x05,0x82,0x04,
0x82,0x02,0x82,0x04,0x82,0x06,0x83,0x03,0x82,0x08,0x82,0x04,0x81,0x09,0x82,
0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x1a,0x82,0x10,0x82,0x06,0x82,0x04,
0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82,
0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x02,
0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x83,
0x02,0x83,0x02,0x83,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,
0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x82,
0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x04,
0x82,0x02,0x82,0x09,0x82,0x03,0x82,0x08,0x82,0x0c,0x82,0x04,0x82,0x02,0x82,
0x11,0x82,0x0e,0x82,0x18,0x82,0x0e,0x82,0x02,0x82,0x0c,0x82,0x0b,0x82,0x0b,
0x82,0x02,0x82,0x0b,0x82,0x4d,0x82,0x45,0x82,0x08,0x82,0x08,0x82,0x05,0x82,
0x02,0x83,0x1a,0x82,0x07,0x81,0x02,0x81,0x07,0x82,0x01,0x82,0x02,0x82,0x01,
0x82,0x05,0x82,0x01,0x84,0x04,0x82,0x01,0x82,0x07,0x82,0x08,0x82,0x08,0x82,
0x06,0x82,0x02,0x82,0x06,0x82,0x28,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x01,
0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x84,0x03,0x82,0x08,0x82,
0x0d,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x19,0x82,0x12,0x82,0x05,
0x82,0x04,0x82,0x02,0x82,0x02,0x84,0x03,0x82,0x02,0x82,0x03,0x82,0x03,0x82,
0x03,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x08,0x82,0x04,
0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x83,0x02,0x83,
0x02,0x84,0x02,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,
0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x0b,0x82,0x05,0x82,0x04,0x82,0x02,0x82,
0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,
0x82,0x04,0x82,0x09,0x82,0x0b,0x82,0x03,0x82,0x04,0x82,0x20,0x82,0x18,0x82,
0x0e,0x82,0x10,0x82,0x0b,0x82,0x0b,0x82,0x02,0x82,0x0b,0x82,0x4d,0x82,0x45,
0x82,0x08,0x82,0x08,0x82,0x26,0x82,0x10,0x88,0x01,0x82,0x01,0x82,0x06,0x83,
0x01,0x82,0x04,0x84,0x08,0x81,0x08,0x82,0x0a,0x82,0x05,0x82,0x02,0x82,0x06,
0x82,0x28,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x08,0x82,0x04,0x82,
0x01,0x82,0x03,0x82,0x08,0x82,0x0d,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04,
0x82,0x18,0x82,0x06,0x88,0x06,0x82,0x04,0x82,0x04,0x82,0x02,0x82,0x01,0x85,
0x02,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x08,0x82,0x04,0x82,0x02,
0x82,0x08,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,
0x02,0x82,0x04,0x82,0x08,0x88,0x02,0x84,0x02,0x82,0x02,0x82,0x04,0x82,0x02,
0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x0b,0x82,
0x05,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x04,0x84,0x06,
0x84,0x08,0x82,0x05,0x82,0x09,0x82,0x0b,0x82,0x2b,0x82,0x18,0x82,0x0e,0x82,
0x10,0x82,0x1c,0x82,0x0b,0x82,0x4d,0x82,0x45,0x82,0x08,0x82,0x08,0x82,0x26,
0x82,0x11,0x82,0x01,0x82,0x03,0x82,0x01,0x82,0x09,0x82,0x06,0x82,0x12,0x82,
0x0a,0x82,0x06,0x84,0x07,0x82,0x27,0x82,0x04,0x82,0x04,0x82,0x05,0x82,0x0b,
0x82,0x07,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x01,0x83,0x04,0x82,0x01,0x83,
0x08,0x82,0x05,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x05,0x83,0x07,0x83,0x05,
0x82,0x16,0x82,0x08,0x82,0x03,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,
0x02,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,
0x82,0x08,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,0x82,
0x08,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,
0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,
0x0a,0x82,0x05,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x01,0x82,0x01,
0x82,0x04,0x84,0x06,0x84,0x08,0x82,0x05,0x82,0x0a,0x82,0x0a,0x82,0x23,0x85,
0x03,0x82,0x01,0x83,0x06,0x85,0x05,0x83,0x01,0x82,0x04,0x84,0x04,0x86,0x05,
0x85,0x01,0x81,0x02,0x82,0x01,0x83,0x05,0x84,0x09,0x84,0x02,0x82,0x03,0x82,
0x06,0x82,0x05,0x81,0x01,0x82,0x01,0x82,0x03,0x82,0x01,0x83,0x06,0x84,0x04,
0x82,0x01,0x83,0x06,0x83,0x01,0x82,0x02,0x82,0x01,0x84,0x04,0x86,0x03,0x86,
0x04,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,
0x82,0x02,0x82,0x04,0x82,0x03,0x87,0x05,0x82,0x08,0x82,0x08,0x82,0x26,0x82,
0x11,0x82,0x01,0x82,0x04,0x86,0x07,0x82,0x05,0x83,0x12,0x82,0x0a,0x82,0x04,
0x88,0x02,0x88,0x0c,0x88,0x10,0x82,0x04,0x82,0x04,0x82,0x05,0x82,0x0a,0x82,
0x06,0x83,0x04,0x82,0x03,0x82,0x03,0x83,0x02,0x82,0x03,0x83,0x02,0x82,0x07,
0x82,0x06,0x84,0x05,0x82,0x02,0x83,0x05,0x83,0x07,0x83,0x04,0x82,0x18,0x82,
0x06,0x82,0x04,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x86,0x04,
0x82,0x08,0x82,0x04,0x82,0x02,0x86,0x04,0x86,0x04,0x82,0x02,0x84,0x02,0x88,
0x05,0x82,0x0a,0x82,0x03,0x85,0x05,0x82,0x08,0x82,0x01,0x82,0x01,0x82,0x02,
0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,
0x04,0x82,0x02,0x82,0x03,0x82,0x05,0x84,0x07,0x82,0x05,0x82,0x04,0x82,0x03,
0x82,0x02,0x82,0x03,0x82,0x01,0x82,0x01,0x82,0x05,0x82,0x08,0x82,0x08,0x82,
0x06,0x82,0x0a,0x82,0x0a,0x82,0x22,0x82,0x03,0x82,0x02,0x83,0x02,0x82,0x04,
0x82,0x03,0x82,0x03,0x82,0x02,0x83,0x03,0x82,0x02,0x82,0x05,0x82,0x06,0x82,
0x03,0x83,0x02,0x83,0x02,0x82,0x06,0x82,0x0b,0x82,0x02,0x82,0x02,0x82,0x07,
0x82,0x05,0x88,0x02,0x83,0x02,0x82,0x04,0x82,0x02,0x82,0x03,0x83,0x02,0x82,
0x04,0x82,0x02,0x83,0x03,0x83,0x02,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x06,
0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,
0x03,0x82,0x04,0x82,0x08,0x82,0x02,0x84,0x09,0x82,0x09,0x84,0x23,0x82,0x11,
0x82,0x01,0x82,0x06,0x82,0x01,0x82,0x05,0x82,0x05,0x82,0x01,0x82,0x11,0x82,
0x0a,0x82,0x06,0x84,0x07,0x82,0x26,0x82,0x05,0x82,0x04,0x82,0x05,0x82,0x08,
0x83,0x09,0x82,0x03,0x82,0x03,0x82,0x09,0x82,0x02,0x82,0x04,0x82,0x05,0x82,
0x06,0x82,0x02,0x82,0x05,0x83,0x01,0x82,0x17,0x82,0x16,0x82,0x06,0x82,0x05,
0x82,0x01,0x82,0x01,0x82,0x02,0x88,0x02,0x82,0x03,0x82,0x03,0x82,0x08,0x82,
0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,
0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x01,0x82,0x01,0x82,
0x02,0x82,0x02,0x84,0x02,0x82,0x04,0x82,0x02,0x86,0x04,0x82,0x04,0x82,0x02,
0x86,0x09,0x82,0x06,0x82,0x05,0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x82,
0x01,0x82,0x04,0x84,0x07,0x82,0x07,0x82,0x07,0x82,0x0b,0x82,0x09,0x82,0x27,
0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,
0x04,0x82,0x06,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,
0x82,0x01,0x82,0x08,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,
0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x07,
0x82,0x0a,0x82,0x06,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x04,0x82,
0x04,0x84,0x04,0x82,0x04,0x82,0x07,0x82,0x06,0x82,0x08,0x82,0x08,0x82,0x26,
0x82,0x0f,0x88,0x05,0x82,0x01,0x82,0x05,0x82,0x05,0x82,0x02,0x82,0x01,0x82,
0x0d,0x82,0x0a,0x82,0x05,0x82,0x02,0x82,0x06,0x82,0x26,0x82,0x05,0x82,0x04,
0x82,0x05,0x82,0x07,0x82,0x0c,0x82,0x02,0x88,0x08,0x82,0x02,0x82,0x04,0x82,
0x05,0x82,0x05,0x82,0x04,0x82,0x08,0x82,0x18,0x82,0x14,0x82,0x07,0x82,0x05,
0x82,0x01,0x84,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,
0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,
0x82,0x0a,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x08,0x82,0x01,0x82,0x01,0x82,
0x02,0x82,0x02,0x84,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,
0x82,0x02,0x82,0x0a,0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x04,0x84,0x04,0x82,
0x01,0x82,0x01,0x82,0x04,0x84,0x07,0x82,0x07,0x82,0x07,0x82,0x0b,0x82,0x09,
0x82,0x22,0x87,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x88,
0x04,0x82,0x06,0x82,0x03,0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,
0x84,0x09,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,
0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x08,0x86,0x05,
0x82,0x06,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x03,0x82,0x01,0x82,0x01,0x82,
0x05,0x82,0x05,0x82,0x04,0x82,0x06,0x82,0x07,0x82,0x08,0x82,0x08,0x82,0x26,
0x82,0x10,0x82,0x01,0x82,0x07,0x82,0x01,0x82,0x04,0x82,0x01,0x83,0x02,0x82,
0x03,0x83,0x0f,0x82,0x08,0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x25,0x82,0x07,
0x82,0x02,0x82,0x06,0x82,0x06,0x82,0x07,0x82,0x04,0x82,0x07,0x82,0x09,0x82,
0x02,0x82,0x04,0x82,0x04,0x82,0x06,0x82,0x04,0x82,0x08,0x82,0x19,0x82,0x05,
0x88,0x05,0x82,0x08,0x82,0x05,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x02,0x82,
0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x08,0x82,0x04,
0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x82,0x03,0x82,0x03,0x82,0x03,0x82,
0x03,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x03,0x83,0x02,0x82,0x04,0x82,0x02,
0x82,0x08,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x03,0x82,0x09,0x82,0x05,0x82,
0x05,0x82,0x04,0x82,0x04,0x84,0x04,0x83,0x02,0x83,0x03,0x82,0x02,0x82,0x06,
0x82,0x06,0x82,0x08,0x82,0x0c,0x82,0x08,0x82,0x21,0x82,0x04,0x82,0x02,0x82,
0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x0a,0x82,0x06,0x82,0x03,
0x82,0x03,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,0x85,0x08,0x82,0x05,0x82,
0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,
0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x0d,0x82,0x04,0x82,0x06,0x82,0x04,0x82,
0x04,0x84,0x04,0x82,0x01,0x82,0x01,0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x05,
0x82,0x08,0x82,0x08,0x82,0x08,0x82,0x38,0x82,0x01,0x82,0x04,0x82,0x01,0x82,
0x01,0x82,0x04,0x84,0x01,0x82,0x01,0x82,0x03,0x82,0x10,0x82,0x08,0x82,0x30,
0x83,0x06,0x82,0x07,0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x08,0x82,0x04,0x82,
0x07,0x82,0x03,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x04,0x82,0x06,0x82,0x04,
0x82,0x03,0x81,0x04,0x82,0x1a,0x82,0x10,0x82,0x10,0x82,0x08,0x82,0x04,0x82,
0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,
0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,0x82,0x05,0x82,0x03,0x82,
0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x03,0x83,0x02,
0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x02,0x84,0x02,0x82,0x03,0x82,0x03,0x82,
0x04,0x82,0x05,0x82,0x05,0x82,0x04,0x82,0x05,0x82,0x05,0x83,0x02,0x83,0x03,
0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x09,0x82,0x0c,0x82,0x08,0x82,0x21,0x82,
0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x0a,
0x82,0x07,0x85,0x04,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,0x82,0x02,0x82,
0x07,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,
0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x0d,0x82,0x04,0x82,
0x06,0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x82,0x01,0x82,0x04,0x84,0x04,
0x82,0x04,0x82,0x04,0x82,0x09,0x82,0x08,0x82,0x08,0x82,0x26,0x82,0x10,0x82,
0x01,0x82,0x05,0x86,0x04,0x82,0x01,0x82,0x01,0x82,0x01,0x83,0x01,0x84,0x10,
0x82,0x06,0x82,0x1d,0x83,0x11,0x83,0x05,0x82,0x09,0x84,0x07,0x82,0x05,0x82,
0x09,0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x04,
0x82,0x08,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x06,0x83,0x07,0x83,0x09,0x82,
0x0e,0x82,0x0a,0x82,0x06,0x82,0x03,0x82,0x02,0x82,0x04,0x82,0x02,0x82,0x03,
0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x03,0x82,0x03,0x82,0x08,0x82,0x09,0x82,
0x02,0x83,0x02,0x82,0x04,0x82,0x05,0x82,0x06,0x82,0x01,0x82,0x04,0x82,0x04,
0x82,0x02,0x82,0x08,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x03,0x82,0x02,0x82,
0x03,0x82,0x09,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x03,0x82,0x02,0x82,0x06,
0x82,0x06,0x82,0x02,0x82,0x06,0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,
0x05,0x82,0x05,0x82,0x09,0x82,0x0d,0x82,0x07,0x82,0x21,0x82,0x04,0x82,0x02,
0x83,0x02,0x82,0x04,0x82,0x03,0x82,0x03,0x82,0x02,0x83,0x03,0x82,0x03,0x82,
0x04,0x82,0x06,0x82,0x08,0x82,0x04,0x82,0x05,0x82,0x0b,0x82,0x02,0x82,0x03,
0x82,0x06,0x82,0x05,0x82,0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x03,0x82,
0x02,0x82,0x03,0x83,0x02,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x07,0x82,0x04,
0x82,0x04,0x82,0x02,0x82,0x03,0x82,0x02,0x83,0x05,0x82,0x05,0x88,0x03,0x82,
0x02,0x82,0x04,0x82,0x02,0x83,0x03,0x82,0x0a,0x82,0x08,0x82,0x08,0x82,0x26,
0x82,0x1c,0x82,0x06,0x82,0x02,0x83,0x03,0x84,0x02,0x82,0x10,0x82,0x04,0x82,
0x1e,0x83,0x11,0x83,0x05,0x82,0x0a,0x82,0x05,0x88,0x02,0x88,0x04,0x84,0x09,
0x82,0x05,0x84,0x06,0x84,0x05,0x82,0x09,0x84,0x06,0x84,0x07,0x83,0x07,0x83,
0x0a,0x81,0x0e,0x81,0x0b,0x82,0x07,0x85,0x03,0x82,0x04,0x82,0x02,0x86,0x06,
0x84,0x04,0x86,0x04,0x88,0x02,0x82,0x0a,0x84,0x01,0x81,0x02,0x82,0x04,0x82,
0x02,0x88,0x04,0x83,0x05,0x82,0x04,0x82,0x02,0x88,0x02,0x82,0x04,0x82,0x02,
0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x0a,0x85,0x03,0x82,0x04,0x82,0x04,0x84,
0x07,0x82,0x07,0x84,0x07,0x82,0x05,0x82,0x04,0x82,0x02,0x82,0x04,0x82,0x05,
0x82,0x05,0x88,0x03,0x86,0x09,0x82,0x03,0x86,0x22,0x85,0x01,0x81,0x02,0x82,
0x01,0x83,0x06,0x85,0x05,0x83,0x01,0x82,0x04,0x85,0x05,0x82,0x07,0x86,0x03,
0x82,0x04,0x82,0x02,0x88,0x08,0x82,0x02,0x82,0x04,0x82,0x02,0x88,0x02,0x82,
0x01,0x82,0x01,0x82,0x02,0x82,0x04,0x82,0x04,0x84,0x04,0x82,0x01,0x83,0x06,
0x83,0x01,0x82,0x03,0x82,0x08,0x86,0x06,0x84,0x05,0x83,0x01,0x82,0x05,0x82,
0x06,0x82,0x02,0x82,0x03,0x82,0x04,0x82,0x04,0x83,0x01,0x82,0x03,0x87,0x06,
0x84,0x05,0x82,0x05,0x84,0x7f,0x15,0x83,0x7f,0x14,0x83,0x7f,0x5e,0x82,0x7f,
0x05,0x89,0x47,0x82,0x04,0x82,0x17,0x82,0x03,0x82,0x34,0x82,0x0e,0x82,0x4e,
0x82,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0a,0x82,0x04,0x82,0x17,0x82,0x03,0x82,
0x34,0x82,0x0e,0x82,0x48,0x82,0x04,0x82,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0a,
0x82,0x04,0x82,0x17,0x82,0x03,0x82,0x34,0x82,0x0e,0x82,0x49,0x82,0x02,0x82,
0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x0c,0x86,0x19,0x85,0x35,0x82,0x0e,0x82,0x4a,
0x84,0x3f,
0x00,
}
};

312
minui/graphics.c Normal file
View file

@ -0,0 +1,312 @@
/*
* 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 <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <linux/fb.h>
#include <linux/kd.h>
#include <pixelflinger/pixelflinger.h>
#include "font_10x18.h"
#include "minui.h"
typedef struct {
GGLSurface texture;
unsigned cwidth;
unsigned cheight;
unsigned ascent;
} GRFont;
static GRFont *gr_font = 0;
static GGLContext *gr_context = 0;
static GGLSurface gr_font_texture;
static GGLSurface gr_framebuffer[2];
static GGLSurface gr_mem_surface;
static unsigned gr_active_fb = 0;
static int gr_fb_fd = -1;
static int gr_vt_fd = -1;
static struct fb_var_screeninfo vi;
static int get_framebuffer(GGLSurface *fb)
{
int fd;
struct fb_fix_screeninfo fi;
void *bits;
fd = open("/dev/graphics/fb0", O_RDWR);
if (fd < 0) {
perror("cannot open fb0");
return -1;
}
if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
perror("failed to get fb0 info");
close(fd);
return -1;
}
if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
perror("failed to get fb0 info");
close(fd);
return -1;
}
bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (bits == MAP_FAILED) {
perror("failed to mmap framebuffer");
close(fd);
return -1;
}
fb->version = sizeof(*fb);
fb->width = vi.xres;
fb->height = vi.yres;
fb->stride = vi.xres;
fb->data = bits;
fb->format = GGL_PIXEL_FORMAT_RGB_565;
fb++;
fb->version = sizeof(*fb);
fb->width = vi.xres;
fb->height = vi.yres;
fb->stride = vi.xres;
fb->data = (void*) (((unsigned) bits) + vi.yres * vi.xres * 2);
fb->format = GGL_PIXEL_FORMAT_RGB_565;
return fd;
}
static void get_memory_surface(GGLSurface* ms) {
ms->version = sizeof(*ms);
ms->width = vi.xres;
ms->height = vi.yres;
ms->stride = vi.xres;
ms->data = malloc(vi.xres * vi.yres * 2);
ms->format = GGL_PIXEL_FORMAT_RGB_565;
}
static void set_active_framebuffer(unsigned n)
{
if (n > 1) return;
vi.yres_virtual = vi.yres * 2;
vi.yoffset = n * vi.yres;
if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
perror("active fb swap failed");
}
}
void gr_flip(void)
{
GGLContext *gl = gr_context;
/* swap front and back buffers */
gr_active_fb = (gr_active_fb + 1) & 1;
/* copy data from the in-memory surface to the buffer we're about
* to make active. */
memcpy(gr_framebuffer[gr_active_fb].data, gr_mem_surface.data,
vi.xres * vi.yres * 2);
/* inform the display driver */
set_active_framebuffer(gr_active_fb);
}
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
GGLContext *gl = gr_context;
GGLint color[4];
color[0] = ((r << 8) | r) + 1;
color[1] = ((g << 8) | g) + 1;
color[2] = ((b << 8) | b) + 1;
color[3] = ((a << 8) | a) + 1;
gl->color4xv(gl, color);
}
int gr_measure(const char *s)
{
return gr_font->cwidth * strlen(s);
}
int gr_text(int x, int y, const char *s)
{
GGLContext *gl = gr_context;
GRFont *font = gr_font;
unsigned off;
y -= font->ascent;
gl->bindTexture(gl, &font->texture);
gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
gl->enable(gl, GGL_TEXTURE_2D);
while((off = *s++)) {
off -= 32;
if (off < 96) {
gl->texCoord2i(gl, (off * font->cwidth) - x, 0 - y);
gl->recti(gl, x, y, x + font->cwidth, y + font->cheight);
}
x += font->cwidth;
}
return x;
}
void gr_fill(int x, int y, int w, int h)
{
GGLContext *gl = gr_context;
gl->disable(gl, GGL_TEXTURE_2D);
gl->recti(gl, x, y, w, h);
}
void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) {
if (gr_context == NULL) {
return;
}
GGLContext *gl = gr_context;
gl->bindTexture(gl, (GGLSurface*) source);
gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
gl->enable(gl, GGL_TEXTURE_2D);
gl->texCoord2i(gl, sx - dx, sy - dy);
gl->recti(gl, dx, dy, dx + w, dy + h);
}
unsigned int gr_get_width(gr_surface surface) {
if (surface == NULL) {
return 0;
}
return ((GGLSurface*) surface)->width;
}
unsigned int gr_get_height(gr_surface surface) {
if (surface == NULL) {
return 0;
}
return ((GGLSurface*) surface)->height;
}
static void gr_init_font(void)
{
GGLSurface *ftex;
unsigned char *bits, *rle;
unsigned char *in, data;
gr_font = calloc(sizeof(*gr_font), 1);
ftex = &gr_font->texture;
bits = malloc(font.width * font.height);
ftex->version = sizeof(*ftex);
ftex->width = font.width;
ftex->height = font.height;
ftex->stride = font.width;
ftex->data = (void*) bits;
ftex->format = GGL_PIXEL_FORMAT_A_8;
in = font.rundata;
while((data = *in++)) {
memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f);
bits += (data & 0x7f);
}
gr_font->cwidth = font.cwidth;
gr_font->cheight = font.cheight;
gr_font->ascent = font.cheight - 2;
}
int gr_init(void)
{
gglInit(&gr_context);
GGLContext *gl = gr_context;
gr_init_font();
gr_vt_fd = open("/dev/tty0", O_RDWR | O_SYNC);
if (gr_vt_fd < 0) {
// This is non-fatal; post-Cupcake kernels don't have tty0.
perror("can't open /dev/tty0");
} else if (ioctl(gr_vt_fd, KDSETMODE, (void*) KD_GRAPHICS)) {
// However, if we do open tty0, we expect the ioctl to work.
perror("failed KDSETMODE to KD_GRAPHICS on tty0");
gr_exit();
return -1;
}
gr_fb_fd = get_framebuffer(gr_framebuffer);
if (gr_fb_fd < 0) {
gr_exit();
return -1;
}
get_memory_surface(&gr_mem_surface);
fprintf(stderr, "framebuffer: fd %d (%d x %d)\n",
gr_fb_fd, gr_framebuffer[0].width, gr_framebuffer[0].height);
/* start with 0 as front (displayed) and 1 as back (drawing) */
gr_active_fb = 0;
set_active_framebuffer(0);
gl->colorBuffer(gl, &gr_mem_surface);
gl->activeTexture(gl, 0);
gl->enable(gl, GGL_BLEND);
gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA);
return 0;
}
void gr_exit(void)
{
close(gr_fb_fd);
gr_fb_fd = -1;
free(gr_mem_surface.data);
ioctl(gr_vt_fd, KDSETMODE, (void*) KD_TEXT);
close(gr_vt_fd);
gr_vt_fd = -1;
}
int gr_fb_width(void)
{
return gr_framebuffer[0].width;
}
int gr_fb_height(void)
{
return gr_framebuffer[0].height;
}
gr_pixel *gr_fb_data(void)
{
return (unsigned short *) gr_mem_surface.data;
}

70
minui/minui.h Normal file
View file

@ -0,0 +1,70 @@
/*
* 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 _MINUI_H_
#define _MINUI_H_
typedef void* gr_surface;
typedef unsigned short gr_pixel;
int gr_init(void);
void gr_exit(void);
int gr_fb_width(void);
int gr_fb_height(void);
gr_pixel *gr_fb_data(void);
void gr_flip(void);
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
void gr_fill(int x, int y, int w, int h);
int gr_text(int x, int y, const char *s);
int gr_measure(const char *s);
void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy);
unsigned int gr_get_width(gr_surface surface);
unsigned int gr_get_height(gr_surface surface);
// input event structure, include <linux/input.h> for the definition.
// see http://www.mjmwired.net/kernel/Documentation/input/ for info.
struct input_event;
// Dream-specific key codes
#define KEY_DREAM_HOME 102 // = KEY_HOME
#define KEY_DREAM_RED 107 // = KEY_END
#define KEY_DREAM_VOLUMEDOWN 114 // = KEY_VOLUMEDOWN
#define KEY_DREAM_VOLUMEUP 115 // = KEY_VOLUMEUP
#define KEY_DREAM_SYM 127 // = KEY_COMPOSE
#define KEY_DREAM_MENU 139 // = KEY_MENU
#define KEY_DREAM_BACK 158 // = KEY_BACK
#define KEY_DREAM_FOCUS 211 // = KEY_HP (light touch on camera)
#define KEY_DREAM_CAMERA 212 // = KEY_CAMERA
#define KEY_DREAM_AT 215 // = KEY_EMAIL
#define KEY_DREAM_GREEN 231
#define KEY_DREAM_FATTOUCH 258 // = BTN_2 ???
#define KEY_DREAM_BALL 272 // = BTN_MOUSE
#define KEY_DREAM_TOUCH 330 // = BTN_TOUCH
int ev_init(void);
void ev_exit(void);
int ev_get(struct input_event *ev, unsigned dont_wait);
// Resources
// Returns 0 if no error, else negative.
int res_create_surface(const char* name, gr_surface* pSurface);
void res_free_surface(gr_surface surface);
#endif

54
minui/mkfont.c Normal file
View file

@ -0,0 +1,54 @@
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv)
{
unsigned n;
unsigned char *x;
unsigned m;
unsigned run_val;
unsigned run_count;
n = gimp_image.width * gimp_image.height;
m = 0;
x = gimp_image.pixel_data;
printf("struct {\n");
printf(" unsigned width;\n");
printf(" unsigned height;\n");
printf(" unsigned cwidth;\n");
printf(" unsigned cheight;\n");
printf(" unsigned char rundata[];\n");
printf("} font = {\n");
printf(" .width = %d,\n .height = %d,\n .cwidth = %d,\n .cheight = %d,\n", gimp_image.width, gimp_image.height,
gimp_image.width / 96, gimp_image.height);
printf(" .rundata = {\n");
run_val = (*x ? 0 : 255);
run_count = 1;
n--;
x+=3;
while(n-- > 0) {
unsigned val = (*x ? 0 : 255);
x+=3;
if((val == run_val) && (run_count < 127)) {
run_count++;
} else {
eject:
printf("0x%02x,",run_count | (run_val ? 0x80 : 0x00));
run_val = val;
run_count = 1;
m += 5;
if(m >= 75) {
printf("\n");
m = 0;
}
}
}
printf("0x%02x,",run_count | (run_val ? 0x80 : 0x00));
printf("\n0x00,");
printf("\n");
printf(" }\n};\n");
return 0;
}

202
minui/resources.c Normal file
View file

@ -0,0 +1,202 @@
/*
* 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 <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <linux/fb.h>
#include <linux/kd.h>
#include <pixelflinger/pixelflinger.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;
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);
resPath[sizeof(resPath)-1] = '\0';
int fd = open(resPath, O_RDONLY);
if (fd == -1) {
result = -1;
goto exit;
}
size_t bytesRead = read(fd, &header, sizeof(header));
if (bytesRead != sizeof(header)) {
result = -2;
goto exit;
}
if (header.bfType != BMP_SIGNATURE) {
result = -3; // Not a legal header
goto exit;
}
if (header.biPlanes != 1) {
result = -4;
goto exit;
}
if (!(header.biBitCount == 24 || header.biBitCount == 32)) {
result = -5;
goto exit;
}
if (header.biCompression != 0) {
result = -6;
goto exit;
}
size_t width = header.biWidth;
size_t height = header.biHeight;
size_t stride = 4 * width;
size_t pixelSize = stride * height;
surface = malloc(sizeof(GGLSurface) + pixelSize);
if (surface == NULL) {
result = -7;
goto exit;
}
unsigned char* pData = (unsigned char*) (surface + 1);
surface->version = sizeof(GGLSurface);
surface->width = width;
surface->height = height;
surface->stride = width; /* Yes, pixels, not bytes */
surface->data = pData;
surface->format = (header.biBitCount == 24) ?
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 x;
for(x = width - 1; x >= 0; x--) {
int sx = x * 3;
int dx = x * 4;
unsigned char b = pRow[sx];
unsigned char g = pRow[sx + 1];
unsigned char r = pRow[sx + 2];
unsigned char a = 0xff;
pRow[dx ] = r; // r
pRow[dx + 1] = g; // g
pRow[dx + 2] = b; // b;
pRow[dx + 3] = a;
}
}
} else { // 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;
}
}
}
*pSurface = (gr_surface) surface;
exit:
if (fd >= 0) {
close(fd);
}
if (result < 0) {
if (surface) {
free(surface);
}
}
return result;
}
void res_free_surface(gr_surface surface) {
GGLSurface* pSurface = (GGLSurface*) surface;
if (pSurface) {
free(pSurface);
}
}

19
minzip/Android.mk Normal file
View file

@ -0,0 +1,19 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
Hash.c \
SysUtil.c \
DirUtil.c \
Inlines.c \
Zip.c
LOCAL_C_INCLUDES += \
external/zlib \
external/safe-iop/include
LOCAL_MODULE := libminzip
LOCAL_CFLAGS += -Wall
include $(BUILD_STATIC_LIBRARY)

357
minzip/Bits.h Normal file
View file

@ -0,0 +1,357 @@
/*
* Copyright 2006 The Android Open Source Project
*
* Some handy functions for manipulating bits and bytes.
*/
#ifndef _MINZIP_BITS
#define _MINZIP_BITS
#include "inline_magic.h"
#include <stdlib.h>
#include <string.h>
/*
* Get 1 byte. (Included to make the code more legible.)
*/
INLINE unsigned char get1(unsigned const char* pSrc)
{
return *pSrc;
}
/*
* Get 2 big-endian bytes.
*/
INLINE unsigned short get2BE(unsigned char const* pSrc)
{
unsigned short result;
result = *pSrc++ << 8;
result |= *pSrc++;
return result;
}
/*
* Get 4 big-endian bytes.
*/
INLINE unsigned int get4BE(unsigned char const* pSrc)
{
unsigned int result;
result = *pSrc++ << 24;
result |= *pSrc++ << 16;
result |= *pSrc++ << 8;
result |= *pSrc++;
return result;
}
/*
* Get 8 big-endian bytes.
*/
INLINE unsigned long long get8BE(unsigned char const* pSrc)
{
unsigned long long result;
result = (unsigned long long) *pSrc++ << 56;
result |= (unsigned long long) *pSrc++ << 48;
result |= (unsigned long long) *pSrc++ << 40;
result |= (unsigned long long) *pSrc++ << 32;
result |= (unsigned long long) *pSrc++ << 24;
result |= (unsigned long long) *pSrc++ << 16;
result |= (unsigned long long) *pSrc++ << 8;
result |= (unsigned long long) *pSrc++;
return result;
}
/*
* Get 2 little-endian bytes.
*/
INLINE unsigned short get2LE(unsigned char const* pSrc)
{
unsigned short result;
result = *pSrc++;
result |= *pSrc++ << 8;
return result;
}
/*
* Get 4 little-endian bytes.
*/
INLINE unsigned int get4LE(unsigned char const* pSrc)
{
unsigned int result;
result = *pSrc++;
result |= *pSrc++ << 8;
result |= *pSrc++ << 16;
result |= *pSrc++ << 24;
return result;
}
/*
* Get 8 little-endian bytes.
*/
INLINE unsigned long long get8LE(unsigned char const* pSrc)
{
unsigned long long result;
result = (unsigned long long) *pSrc++;
result |= (unsigned long long) *pSrc++ << 8;
result |= (unsigned long long) *pSrc++ << 16;
result |= (unsigned long long) *pSrc++ << 24;
result |= (unsigned long long) *pSrc++ << 32;
result |= (unsigned long long) *pSrc++ << 40;
result |= (unsigned long long) *pSrc++ << 48;
result |= (unsigned long long) *pSrc++ << 56;
return result;
}
/*
* Grab 1 byte and advance the data pointer.
*/
INLINE unsigned char read1(unsigned const char** ppSrc)
{
return *(*ppSrc)++;
}
/*
* Grab 2 big-endian bytes and advance the data pointer.
*/
INLINE unsigned short read2BE(unsigned char const** ppSrc)
{
unsigned short result;
result = *(*ppSrc)++ << 8;
result |= *(*ppSrc)++;
return result;
}
/*
* Grab 4 big-endian bytes and advance the data pointer.
*/
INLINE unsigned int read4BE(unsigned char const** ppSrc)
{
unsigned int result;
result = *(*ppSrc)++ << 24;
result |= *(*ppSrc)++ << 16;
result |= *(*ppSrc)++ << 8;
result |= *(*ppSrc)++;
return result;
}
/*
* Get 8 big-endian bytes.
*/
INLINE unsigned long long read8BE(unsigned char const** ppSrc)
{
unsigned long long result;
result = (unsigned long long) *(*ppSrc)++ << 56;
result |= (unsigned long long) *(*ppSrc)++ << 48;
result |= (unsigned long long) *(*ppSrc)++ << 40;
result |= (unsigned long long) *(*ppSrc)++ << 32;
result |= (unsigned long long) *(*ppSrc)++ << 24;
result |= (unsigned long long) *(*ppSrc)++ << 16;
result |= (unsigned long long) *(*ppSrc)++ << 8;
result |= (unsigned long long) *(*ppSrc)++;
return result;
}
/*
* Grab 2 little-endian bytes and advance the data pointer.
*/
INLINE unsigned short read2LE(unsigned char const** ppSrc)
{
unsigned short result;
result = *(*ppSrc)++;
result |= *(*ppSrc)++ << 8;
return result;
}
/*
* Grab 4 little-endian bytes and advance the data pointer.
*/
INLINE unsigned int read4LE(unsigned char const** ppSrc)
{
unsigned int result;
result = *(*ppSrc)++;
result |= *(*ppSrc)++ << 8;
result |= *(*ppSrc)++ << 16;
result |= *(*ppSrc)++ << 24;
return result;
}
/*
* Get 8 little-endian bytes.
*/
INLINE unsigned long long read8LE(unsigned char const** ppSrc)
{
unsigned long long result;
result = (unsigned long long) *(*ppSrc)++;
result |= (unsigned long long) *(*ppSrc)++ << 8;
result |= (unsigned long long) *(*ppSrc)++ << 16;
result |= (unsigned long long) *(*ppSrc)++ << 24;
result |= (unsigned long long) *(*ppSrc)++ << 32;
result |= (unsigned long long) *(*ppSrc)++ << 40;
result |= (unsigned long long) *(*ppSrc)++ << 48;
result |= (unsigned long long) *(*ppSrc)++ << 56;
return result;
}
/*
* Skip over a UTF-8 string.
*/
INLINE void skipUtf8String(unsigned char const** ppSrc)
{
unsigned int length = read4BE(ppSrc);
(*ppSrc) += length;
}
/*
* Read a UTF-8 string into a fixed-size buffer, and null-terminate it.
*
* Returns the length of the original string.
*/
INLINE int readUtf8String(unsigned char const** ppSrc, char* buf, size_t bufLen)
{
unsigned int length = read4BE(ppSrc);
size_t copyLen = (length < bufLen) ? length : bufLen-1;
memcpy(buf, *ppSrc, copyLen);
buf[copyLen] = '\0';
(*ppSrc) += length;
return length;
}
/*
* Read a UTF-8 string into newly-allocated storage, and null-terminate it.
*
* Returns the string and its length. (The latter is probably unnecessary
* for the way we're using UTF8.)
*/
INLINE char* readNewUtf8String(unsigned char const** ppSrc, size_t* pLength)
{
unsigned int length = read4BE(ppSrc);
char* buf;
buf = (char*) malloc(length+1);
memcpy(buf, *ppSrc, length);
buf[length] = '\0';
(*ppSrc) += length;
*pLength = length;
return buf;
}
/*
* Set 1 byte. (Included to make the code more legible.)
*/
INLINE void set1(unsigned char* buf, unsigned char val)
{
*buf = (unsigned char)(val);
}
/*
* Set 2 big-endian bytes.
*/
INLINE void set2BE(unsigned char* buf, unsigned short val)
{
*buf++ = (unsigned char)(val >> 8);
*buf = (unsigned char)(val);
}
/*
* Set 4 big-endian bytes.
*/
INLINE void set4BE(unsigned char* buf, unsigned int val)
{
*buf++ = (unsigned char)(val >> 24);
*buf++ = (unsigned char)(val >> 16);
*buf++ = (unsigned char)(val >> 8);
*buf = (unsigned char)(val);
}
/*
* Set 8 big-endian bytes.
*/
INLINE void set8BE(unsigned char* buf, unsigned long long val)
{
*buf++ = (unsigned char)(val >> 56);
*buf++ = (unsigned char)(val >> 48);
*buf++ = (unsigned char)(val >> 40);
*buf++ = (unsigned char)(val >> 32);
*buf++ = (unsigned char)(val >> 24);
*buf++ = (unsigned char)(val >> 16);
*buf++ = (unsigned char)(val >> 8);
*buf = (unsigned char)(val);
}
/*
* Set 2 little-endian bytes.
*/
INLINE void set2LE(unsigned char* buf, unsigned short val)
{
*buf++ = (unsigned char)(val);
*buf = (unsigned char)(val >> 8);
}
/*
* Set 4 little-endian bytes.
*/
INLINE void set4LE(unsigned char* buf, unsigned int val)
{
*buf++ = (unsigned char)(val);
*buf++ = (unsigned char)(val >> 8);
*buf++ = (unsigned char)(val >> 16);
*buf = (unsigned char)(val >> 24);
}
/*
* Set 8 little-endian bytes.
*/
INLINE void set8LE(unsigned char* buf, unsigned long long val)
{
*buf++ = (unsigned char)(val);
*buf++ = (unsigned char)(val >> 8);
*buf++ = (unsigned char)(val >> 16);
*buf++ = (unsigned char)(val >> 24);
*buf++ = (unsigned char)(val >> 32);
*buf++ = (unsigned char)(val >> 40);
*buf++ = (unsigned char)(val >> 48);
*buf = (unsigned char)(val >> 56);
}
/*
* Stuff a UTF-8 string into the buffer.
*/
INLINE void setUtf8String(unsigned char* buf, const unsigned char* str)
{
unsigned int strLen = strlen((const char*)str);
set4BE(buf, strLen);
memcpy(buf + sizeof(unsigned int), str, strLen);
}
#endif /*_MINZIP_BITS*/

280
minzip/DirUtil.c Normal file
View file

@ -0,0 +1,280 @@
/*
* 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 <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
#include <limits.h>
#include "DirUtil.h"
typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus;
static DirStatus
getPathDirStatus(const char *path)
{
struct stat st;
int err;
err = stat(path, &st);
if (err == 0) {
/* Something's there; make sure it's a directory.
*/
if (S_ISDIR(st.st_mode)) {
return DDIR;
}
errno = ENOTDIR;
return DILLEGAL;
} else if (errno != ENOENT) {
/* Something went wrong, or something in the path
* is bad. Can't do anything in this situation.
*/
return DILLEGAL;
}
return DMISSING;
}
int
dirCreateHierarchy(const char *path, int mode,
const struct utimbuf *timestamp, bool stripFileName)
{
DirStatus ds;
/* Check for an empty string before we bother
* making any syscalls.
*/
if (path[0] == '\0') {
errno = ENOENT;
return -1;
}
/* Allocate a path that we can modify; stick a slash on
* the end to make things easier.
*/
size_t pathLen = strlen(path);
char *cpath = (char *)malloc(pathLen + 2);
if (cpath == NULL) {
errno = ENOMEM;
return -1;
}
memcpy(cpath, path, pathLen);
if (stripFileName) {
/* Strip everything after the last slash.
*/
char *c = cpath + pathLen - 1;
while (c != cpath && *c != '/') {
c--;
}
if (c == cpath) {
//xxx test this path
/* No directory component. Act like the path was empty.
*/
errno = ENOENT;
free(cpath);
return -1;
}
c[1] = '\0'; // Terminate after the slash we found.
} else {
/* Make sure that the path ends in a slash.
*/
cpath[pathLen] = '/';
cpath[pathLen + 1] = '\0';
}
/* See if it already exists.
*/
ds = getPathDirStatus(cpath);
if (ds == DDIR) {
return 0;
} else if (ds == DILLEGAL) {
return -1;
}
/* Walk up the path from the root and make each level.
* If a directory already exists, no big deal.
*/
char *p = cpath;
while (*p != '\0') {
/* Skip any slashes, watching out for the end of the string.
*/
while (*p != '\0' && *p == '/') {
p++;
}
if (*p == '\0') {
break;
}
/* Find the end of the next path component.
* We know that we'll see a slash before the NUL,
* because we added it, above.
*/
while (*p != '/') {
p++;
}
*p = '\0';
/* Check this part of the path and make a new directory
* if necessary.
*/
ds = getPathDirStatus(cpath);
if (ds == DILLEGAL) {
/* Could happen if some other process/thread is
* messing with the filesystem.
*/
free(cpath);
return -1;
} else if (ds == DMISSING) {
int err;
err = mkdir(cpath, mode);
if (err != 0) {
free(cpath);
return -1;
}
if (timestamp != NULL && utime(cpath, timestamp)) {
free(cpath);
return -1;
}
}
// else, this directory already exists.
/* Repair the path and continue.
*/
*p = '/';
}
free(cpath);
return 0;
}
int
dirUnlinkHierarchy(const char *path)
{
struct stat st;
DIR *dir;
struct dirent *de;
int fail = 0;
/* is it a file or directory? */
if (lstat(path, &st) < 0) {
return -1;
}
/* a file, so unlink it */
if (!S_ISDIR(st.st_mode)) {
return unlink(path);
}
/* a directory, so open handle */
dir = opendir(path);
if (dir == NULL) {
return -1;
}
/* recurse over components */
errno = 0;
while ((de = readdir(dir)) != NULL) {
//TODO: don't blow the stack
char dn[PATH_MAX];
if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
continue;
}
snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
if (dirUnlinkHierarchy(dn) < 0) {
fail = 1;
break;
}
errno = 0;
}
/* in case readdir or unlink_recursive failed */
if (fail || errno < 0) {
int save = errno;
closedir(dir);
errno = save;
return -1;
}
/* close directory handle */
if (closedir(dir) < 0) {
return -1;
}
/* delete target directory */
return rmdir(path);
}
int
dirSetHierarchyPermissions(const char *path,
int uid, int gid, int dirMode, int fileMode)
{
struct stat st;
if (lstat(path, &st)) {
return -1;
}
/* ignore symlinks */
if (S_ISLNK(st.st_mode)) {
return 0;
}
/* directories and files get different permissions */
if (chown(path, uid, gid) ||
chmod(path, S_ISDIR(st.st_mode) ? dirMode : fileMode)) {
return -1;
}
/* recurse over directory components */
if (S_ISDIR(st.st_mode)) {
DIR *dir = opendir(path);
if (dir == NULL) {
return -1;
}
errno = 0;
const struct dirent *de;
while (errno == 0 && (de = readdir(dir)) != NULL) {
if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) {
continue;
}
char dn[PATH_MAX];
snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name);
if (!dirSetHierarchyPermissions(dn, uid, gid, dirMode, fileMode)) {
errno = 0;
} else if (errno == 0) {
errno = -1;
}
}
if (errno != 0) {
int save = errno;
closedir(dir);
errno = save;
return -1;
}
if (closedir(dir)) {
return -1;
}
}
return 0;
}

51
minzip/DirUtil.h Normal file
View file

@ -0,0 +1,51 @@
/*
* 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 MINZIP_DIRUTIL_H_
#define MINZIP_DIRUTIL_H_
#include <stdbool.h>
#include <utime.h>
/* Like "mkdir -p", try to guarantee that all directories
* specified in path are present, creating as many directories
* as necessary. The specified mode is passed to all mkdir
* calls; no modifications are made to umask.
*
* If stripFileName is set, everything after the final '/'
* is stripped before creating the directory hierarchy.
*
* If timestamp is non-NULL, new directories will be timestamped accordingly.
*
* Returns 0 on success; returns -1 (and sets errno) on failure
* (usually if some element of path is not a directory).
*/
int dirCreateHierarchy(const char *path, int mode,
const struct utimbuf *timestamp, bool stripFileName);
/* rm -rf <path>
*/
int dirUnlinkHierarchy(const char *path);
/* chown -R <uid>:<gid> <path>
* chmod -R <mode> <path>
*
* Sets directories to <dirMode> and files to <fileMode>. Skips symlinks.
*/
int dirSetHierarchyPermissions(const char *path,
int uid, int gid, int dirMode, int fileMode);
#endif // MINZIP_DIRUTIL_H_

390
minzip/Hash.c Normal file
View file

@ -0,0 +1,390 @@
/*
* Copyright 2006 The Android Open Source Project
*
* Hash table. The dominant calls are add and lookup, with removals
* happening very infrequently. We use probing, and don't worry much
* about tombstone removal.
*/
#include <stdlib.h>
#include <assert.h>
#define LOG_TAG "minzip"
#include "Log.h"
#include "Hash.h"
/* table load factor, i.e. how full can it get before we resize */
//#define LOAD_NUMER 3 // 75%
//#define LOAD_DENOM 4
#define LOAD_NUMER 5 // 62.5%
#define LOAD_DENOM 8
//#define LOAD_NUMER 1 // 50%
//#define LOAD_DENOM 2
/*
* Compute the capacity needed for a table to hold "size" elements.
*/
size_t mzHashSize(size_t size) {
return (size * LOAD_DENOM) / LOAD_NUMER +1;
}
/*
* Round up to the next highest power of 2.
*
* Found on http://graphics.stanford.edu/~seander/bithacks.html.
*/
unsigned int roundUpPower2(unsigned int val)
{
val--;
val |= val >> 1;
val |= val >> 2;
val |= val >> 4;
val |= val >> 8;
val |= val >> 16;
val++;
return val;
}
/*
* Create and initialize a hash table.
*/
HashTable* mzHashTableCreate(size_t initialSize, HashFreeFunc freeFunc)
{
HashTable* pHashTable;
assert(initialSize > 0);
pHashTable = (HashTable*) malloc(sizeof(*pHashTable));
if (pHashTable == NULL)
return NULL;
pHashTable->tableSize = roundUpPower2(initialSize);
pHashTable->numEntries = pHashTable->numDeadEntries = 0;
pHashTable->freeFunc = freeFunc;
pHashTable->pEntries =
(HashEntry*) calloc((size_t)pHashTable->tableSize, sizeof(HashTable));
if (pHashTable->pEntries == NULL) {
free(pHashTable);
return NULL;
}
return pHashTable;
}
/*
* Clear out all entries.
*/
void mzHashTableClear(HashTable* pHashTable)
{
HashEntry* pEnt;
int i;
pEnt = pHashTable->pEntries;
for (i = 0; i < pHashTable->tableSize; i++, pEnt++) {
if (pEnt->data == HASH_TOMBSTONE) {
// nuke entry
pEnt->data = NULL;
} else if (pEnt->data != NULL) {
// call free func then nuke entry
if (pHashTable->freeFunc != NULL)
(*pHashTable->freeFunc)(pEnt->data);
pEnt->data = NULL;
}
}
pHashTable->numEntries = 0;
pHashTable->numDeadEntries = 0;
}
/*
* Free the table.
*/
void mzHashTableFree(HashTable* pHashTable)
{
if (pHashTable == NULL)
return;
mzHashTableClear(pHashTable);
free(pHashTable->pEntries);
free(pHashTable);
}
#ifndef NDEBUG
/*
* Count up the number of tombstone entries in the hash table.
*/
static int countTombStones(HashTable* pHashTable)
{
int i, count;
for (count = i = 0; i < pHashTable->tableSize; i++) {
if (pHashTable->pEntries[i].data == HASH_TOMBSTONE)
count++;
}
return count;
}
#endif
/*
* Resize a hash table. We do this when adding an entry increased the
* size of the table beyond its comfy limit.
*
* This essentially requires re-inserting all elements into the new storage.
*
* If multiple threads can access the hash table, the table's lock should
* have been grabbed before issuing the "lookup+add" call that led to the
* resize, so we don't have a synchronization problem here.
*/
static bool resizeHash(HashTable* pHashTable, int newSize)
{
HashEntry* pNewEntries;
int i;
assert(countTombStones(pHashTable) == pHashTable->numDeadEntries);
//LOGI("before: dead=%d\n", pHashTable->numDeadEntries);
pNewEntries = (HashEntry*) calloc(newSize, sizeof(HashTable));
if (pNewEntries == NULL)
return false;
for (i = 0; i < pHashTable->tableSize; i++) {
void* data = pHashTable->pEntries[i].data;
if (data != NULL && data != HASH_TOMBSTONE) {
int hashValue = pHashTable->pEntries[i].hashValue;
int newIdx;
/* probe for new spot, wrapping around */
newIdx = hashValue & (newSize-1);
while (pNewEntries[newIdx].data != NULL)
newIdx = (newIdx + 1) & (newSize-1);
pNewEntries[newIdx].hashValue = hashValue;
pNewEntries[newIdx].data = data;
}
}
free(pHashTable->pEntries);
pHashTable->pEntries = pNewEntries;
pHashTable->tableSize = newSize;
pHashTable->numDeadEntries = 0;
assert(countTombStones(pHashTable) == 0);
return true;
}
/*
* Look up an entry.
*
* We probe on collisions, wrapping around the table.
*/
void* mzHashTableLookup(HashTable* pHashTable, unsigned int itemHash, void* item,
HashCompareFunc cmpFunc, bool doAdd)
{
HashEntry* pEntry;
HashEntry* pEnd;
void* result = NULL;
assert(pHashTable->tableSize > 0);
assert(item != HASH_TOMBSTONE);
assert(item != NULL);
/* jump to the first entry and probe for a match */
pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
pEnd = &pHashTable->pEntries[pHashTable->tableSize];
while (pEntry->data != NULL) {
if (pEntry->data != HASH_TOMBSTONE &&
pEntry->hashValue == itemHash &&
(*cmpFunc)(pEntry->data, item) == 0)
{
/* match */
//LOGD("+++ match on entry %d\n", pEntry - pHashTable->pEntries);
break;
}
pEntry++;
if (pEntry == pEnd) { /* wrap around to start */
if (pHashTable->tableSize == 1)
break; /* edge case - single-entry table */
pEntry = pHashTable->pEntries;
}
//LOGI("+++ look probing %d...\n", pEntry - pHashTable->pEntries);
}
if (pEntry->data == NULL) {
if (doAdd) {
pEntry->hashValue = itemHash;
pEntry->data = item;
pHashTable->numEntries++;
/*
* We've added an entry. See if this brings us too close to full.
*/
if ((pHashTable->numEntries+pHashTable->numDeadEntries) * LOAD_DENOM
> pHashTable->tableSize * LOAD_NUMER)
{
if (!resizeHash(pHashTable, pHashTable->tableSize * 2)) {
/* don't really have a way to indicate failure */
LOGE("Dalvik hash resize failure\n");
abort();
}
/* note "pEntry" is now invalid */
} else {
//LOGW("okay %d/%d/%d\n",
// pHashTable->numEntries, pHashTable->tableSize,
// (pHashTable->tableSize * LOAD_NUMER) / LOAD_DENOM);
}
/* full table is bad -- search for nonexistent never halts */
assert(pHashTable->numEntries < pHashTable->tableSize);
result = item;
} else {
assert(result == NULL);
}
} else {
result = pEntry->data;
}
return result;
}
/*
* Remove an entry from the table.
*
* Does NOT invoke the "free" function on the item.
*/
bool mzHashTableRemove(HashTable* pHashTable, unsigned int itemHash, void* item)
{
HashEntry* pEntry;
HashEntry* pEnd;
assert(pHashTable->tableSize > 0);
/* jump to the first entry and probe for a match */
pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
pEnd = &pHashTable->pEntries[pHashTable->tableSize];
while (pEntry->data != NULL) {
if (pEntry->data == item) {
//LOGI("+++ stepping on entry %d\n", pEntry - pHashTable->pEntries);
pEntry->data = HASH_TOMBSTONE;
pHashTable->numEntries--;
pHashTable->numDeadEntries++;
return true;
}
pEntry++;
if (pEntry == pEnd) { /* wrap around to start */
if (pHashTable->tableSize == 1)
break; /* edge case - single-entry table */
pEntry = pHashTable->pEntries;
}
//LOGI("+++ del probing %d...\n", pEntry - pHashTable->pEntries);
}
return false;
}
/*
* Execute a function on every entry in the hash table.
*
* If "func" returns a nonzero value, terminate early and return the value.
*/
int mzHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg)
{
int i, val;
for (i = 0; i < pHashTable->tableSize; i++) {
HashEntry* pEnt = &pHashTable->pEntries[i];
if (pEnt->data != NULL && pEnt->data != HASH_TOMBSTONE) {
val = (*func)(pEnt->data, arg);
if (val != 0)
return val;
}
}
return 0;
}
/*
* Look up an entry, counting the number of times we have to probe.
*
* Returns -1 if the entry wasn't found.
*/
int countProbes(HashTable* pHashTable, unsigned int itemHash, const void* item,
HashCompareFunc cmpFunc)
{
HashEntry* pEntry;
HashEntry* pEnd;
int count = 0;
assert(pHashTable->tableSize > 0);
assert(item != HASH_TOMBSTONE);
assert(item != NULL);
/* jump to the first entry and probe for a match */
pEntry = &pHashTable->pEntries[itemHash & (pHashTable->tableSize-1)];
pEnd = &pHashTable->pEntries[pHashTable->tableSize];
while (pEntry->data != NULL) {
if (pEntry->data != HASH_TOMBSTONE &&
pEntry->hashValue == itemHash &&
(*cmpFunc)(pEntry->data, item) == 0)
{
/* match */
break;
}
pEntry++;
if (pEntry == pEnd) { /* wrap around to start */
if (pHashTable->tableSize == 1)
break; /* edge case - single-entry table */
pEntry = pHashTable->pEntries;
}
count++;
}
if (pEntry->data == NULL)
return -1;
return count;
}
/*
* Evaluate the amount of probing required for the specified hash table.
*
* We do this by running through all entries in the hash table, computing
* the hash value and then doing a lookup.
*
* The caller should lock the table before calling here.
*/
void mzHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc,
HashCompareFunc cmpFunc)
{
int numEntries, minProbe, maxProbe, totalProbe;
HashIter iter;
numEntries = maxProbe = totalProbe = 0;
minProbe = 65536*32767;
for (mzHashIterBegin(pHashTable, &iter); !mzHashIterDone(&iter);
mzHashIterNext(&iter))
{
const void* data = (const void*)mzHashIterData(&iter);
int count;
count = countProbes(pHashTable, (*calcFunc)(data), data, cmpFunc);
numEntries++;
if (count < minProbe)
minProbe = count;
if (count > maxProbe)
maxProbe = count;
totalProbe += count;
}
LOGI("Probe: min=%d max=%d, total=%d in %d (%d), avg=%.3f\n",
minProbe, maxProbe, totalProbe, numEntries, pHashTable->tableSize,
(float) totalProbe / (float) numEntries);
}

186
minzip/Hash.h Normal file
View file

@ -0,0 +1,186 @@
/*
* Copyright 2007 The Android Open Source Project
*
* General purpose hash table, used for finding classes, methods, etc.
*
* When the number of elements reaches 3/4 of the table's capacity, the
* table will be resized.
*/
#ifndef _MINZIP_HASH
#define _MINZIP_HASH
#include "inline_magic.h"
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
/* compute the hash of an item with a specific type */
typedef unsigned int (*HashCompute)(const void* item);
/*
* Compare a hash entry with a "loose" item after their hash values match.
* Returns { <0, 0, >0 } depending on ordering of items (same semantics
* as strcmp()).
*/
typedef int (*HashCompareFunc)(const void* tableItem, const void* looseItem);
/*
* This function will be used to free entries in the table. This can be
* NULL if no free is required, free(), or a custom function.
*/
typedef void (*HashFreeFunc)(void* ptr);
/*
* Used by mzHashForeach().
*/
typedef int (*HashForeachFunc)(void* data, void* arg);
/*
* One entry in the hash table. "data" values are expected to be (or have
* the same characteristics as) valid pointers. In particular, a NULL
* value for "data" indicates an empty slot, and HASH_TOMBSTONE indicates
* a no-longer-used slot that must be stepped over during probing.
*
* Attempting to add a NULL or tombstone value is an error.
*
* When an entry is released, we will call (HashFreeFunc)(entry->data).
*/
typedef struct HashEntry {
unsigned int hashValue;
void* data;
} HashEntry;
#define HASH_TOMBSTONE ((void*) 0xcbcacccd) // invalid ptr value
/*
* Expandable hash table.
*
* This structure should be considered opaque.
*/
typedef struct HashTable {
int tableSize; /* must be power of 2 */
int numEntries; /* current #of "live" entries */
int numDeadEntries; /* current #of tombstone entries */
HashEntry* pEntries; /* array on heap */
HashFreeFunc freeFunc;
} HashTable;
/*
* Create and initialize a HashTable structure, using "initialSize" as
* a basis for the initial capacity of the table. (The actual initial
* table size may be adjusted upward.) If you know exactly how many
* elements the table will hold, pass the result from mzHashSize() in.)
*
* Returns "false" if unable to allocate the table.
*/
HashTable* mzHashTableCreate(size_t initialSize, HashFreeFunc freeFunc);
/*
* Compute the capacity needed for a table to hold "size" elements. Use
* this when you know ahead of time how many elements the table will hold.
* Pass this value into mzHashTableCreate() to ensure that you can add
* all elements without needing to reallocate the table.
*/
size_t mzHashSize(size_t size);
/*
* Clear out a hash table, freeing the contents of any used entries.
*/
void mzHashTableClear(HashTable* pHashTable);
/*
* Free a hash table.
*/
void mzHashTableFree(HashTable* pHashTable);
/*
* Get #of entries in hash table.
*/
INLINE int mzHashTableNumEntries(HashTable* pHashTable) {
return pHashTable->numEntries;
}
/*
* Get total size of hash table (for memory usage calculations).
*/
INLINE int mzHashTableMemUsage(HashTable* pHashTable) {
return sizeof(HashTable) + pHashTable->tableSize * sizeof(HashEntry);
}
/*
* Look up an entry in the table, possibly adding it if it's not there.
*
* If "item" is not found, and "doAdd" is false, NULL is returned.
* Otherwise, a pointer to the found or added item is returned. (You can
* tell the difference by seeing if return value == item.)
*
* An "add" operation may cause the entire table to be reallocated.
*/
void* mzHashTableLookup(HashTable* pHashTable, unsigned int itemHash, void* item,
HashCompareFunc cmpFunc, bool doAdd);
/*
* Remove an item from the hash table, given its "data" pointer. Does not
* invoke the "free" function; just detaches it from the table.
*/
bool mzHashTableRemove(HashTable* pHashTable, unsigned int hash, void* item);
/*
* Execute "func" on every entry in the hash table.
*
* If "func" returns a nonzero value, terminate early and return the value.
*/
int mzHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg);
/*
* An alternative to mzHashForeach(), using an iterator.
*
* Use like this:
* HashIter iter;
* for (mzHashIterBegin(hashTable, &iter); !mzHashIterDone(&iter);
* mzHashIterNext(&iter))
* {
* MyData* data = (MyData*)mzHashIterData(&iter);
* }
*/
typedef struct HashIter {
void* data;
HashTable* pHashTable;
int idx;
} HashIter;
INLINE void mzHashIterNext(HashIter* pIter) {
int i = pIter->idx +1;
int lim = pIter->pHashTable->tableSize;
for ( ; i < lim; i++) {
void* data = pIter->pHashTable->pEntries[i].data;
if (data != NULL && data != HASH_TOMBSTONE)
break;
}
pIter->idx = i;
}
INLINE void mzHashIterBegin(HashTable* pHashTable, HashIter* pIter) {
pIter->pHashTable = pHashTable;
pIter->idx = -1;
mzHashIterNext(pIter);
}
INLINE bool mzHashIterDone(HashIter* pIter) {
return (pIter->idx >= pIter->pHashTable->tableSize);
}
INLINE void* mzHashIterData(HashIter* pIter) {
assert(pIter->idx >= 0 && pIter->idx < pIter->pHashTable->tableSize);
return pIter->pHashTable->pEntries[pIter->idx].data;
}
/*
* Evaluate hash table performance by examining the number of times we
* have to probe for an entry.
*
* The caller should lock the table beforehand.
*/
typedef unsigned int (*HashCalcFunc)(const void* item);
void mzHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc,
HashCompareFunc cmpFunc);
#endif /*_MINZIP_HASH*/

25
minzip/Inlines.c Normal file
View file

@ -0,0 +1,25 @@
/*
* 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.
*/
/* Make sure that non-inlined versions of INLINED-marked functions
* exist so that debug builds (which don't generally do inlining)
* don't break.
*/
#define MINZIP_GENERATE_INLINES 1
#include "Bits.h"
#include "Hash.h"
#include "SysUtil.h"
#include "Zip.h"

207
minzip/Log.h Normal file
View file

@ -0,0 +1,207 @@
//
// Copyright 2005 The Android Open Source Project
//
// C/C++ logging functions. See the logging documentation for API details.
//
// We'd like these to be available from C code (in case we import some from
// somewhere), so this has a C interface.
//
// The output will be correct when the log file is shared between multiple
// threads and/or multiple processes so long as the operating system
// supports O_APPEND. These calls have mutex-protected data structures
// and so are NOT reentrant. Do not use LOG in a signal handler.
//
#ifndef _MINZIP_LOG_H
#define _MINZIP_LOG_H
#include <stdio.h>
// ---------------------------------------------------------------------
/*
* Normally we strip LOGV (VERBOSE messages) from release builds.
* You can modify this (for example with "#define LOG_NDEBUG 0"
* at the top of your source file) to change that behavior.
*/
#ifndef LOG_NDEBUG
#ifdef NDEBUG
#define LOG_NDEBUG 1
#else
#define LOG_NDEBUG 0
#endif
#endif
/*
* This is the local tag used for the following simplified
* logging macros. You can change this preprocessor definition
* before using the other macros to change the tag.
*/
#ifndef LOG_TAG
#define LOG_TAG NULL
#endif
// ---------------------------------------------------------------------
/*
* Simplified macro to send a verbose log message using the current LOG_TAG.
*/
#ifndef LOGV
#if LOG_NDEBUG
#define LOGV(...) ((void)0)
#else
#define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#endif
#endif
#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
#ifndef LOGV_IF
#if LOG_NDEBUG
#define LOGV_IF(cond, ...) ((void)0)
#else
#define LOGV_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
#endif
#define LOGVV LOGV
#define LOGVV_IF LOGV_IF
/*
* Simplified macro to send a debug log message using the current LOG_TAG.
*/
#ifndef LOGD
#define LOGD(...) ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#endif
#ifndef LOGD_IF
#define LOGD_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Simplified macro to send an info log message using the current LOG_TAG.
*/
#ifndef LOGI
#define LOGI(...) ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
#endif
#ifndef LOGI_IF
#define LOGI_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Simplified macro to send a warning log message using the current LOG_TAG.
*/
#ifndef LOGW
#define LOGW(...) ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
#endif
#ifndef LOGW_IF
#define LOGW_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Simplified macro to send an error log message using the current LOG_TAG.
*/
#ifndef LOGE
#define LOGE(...) ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
#endif
#ifndef LOGE_IF
#define LOGE_IF(cond, ...) \
( (CONDITION(cond)) \
? ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
: (void)0 )
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* verbose priority.
*/
#ifndef IF_LOGV
#if LOG_NDEBUG
#define IF_LOGV() if (false)
#else
#define IF_LOGV() IF_LOG(LOG_VERBOSE, LOG_TAG)
#endif
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* debug priority.
*/
#ifndef IF_LOGD
#define IF_LOGD() IF_LOG(LOG_DEBUG, LOG_TAG)
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* info priority.
*/
#ifndef IF_LOGI
#define IF_LOGI() IF_LOG(LOG_INFO, LOG_TAG)
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* warn priority.
*/
#ifndef IF_LOGW
#define IF_LOGW() IF_LOG(LOG_WARN, LOG_TAG)
#endif
/*
* Conditional based on whether the current LOG_TAG is enabled at
* error priority.
*/
#ifndef IF_LOGE
#define IF_LOGE() IF_LOG(LOG_ERROR, LOG_TAG)
#endif
// ---------------------------------------------------------------------
/*
* Basic log message macro.
*
* Example:
* LOG(LOG_WARN, NULL, "Failed with error %d", errno);
*
* The second argument may be NULL or "" to indicate the "global" tag.
*
* Non-gcc probably won't have __FUNCTION__. It's not vital. gcc also
* offers __PRETTY_FUNCTION__, which is rather more than we need.
*/
#ifndef LOG
#define LOG(priority, tag, ...) \
LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
#endif
/*
* Log macro that allows you to specify a number for the priority.
*/
#ifndef LOG_PRI
#define LOG_PRI(priority, tag, ...) \
printf(tag ": " __VA_ARGS__)
#endif
/*
* Conditional given a desired logging priority and tag.
*/
#ifndef IF_LOG
#define IF_LOG(priority, tag) \
if (1)
#endif
#endif // _MINZIP_LOG_H

212
minzip/SysUtil.c Normal file
View file

@ -0,0 +1,212 @@
/*
* Copyright 2006 The Android Open Source Project
*
* System utilities.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <limits.h>
#include <errno.h>
#include <assert.h>
#define LOG_TAG "minzip"
#include "Log.h"
#include "SysUtil.h"
/*
* Having trouble finding a portable way to get this. sysconf(_SC_PAGE_SIZE)
* seems appropriate, but we don't have that on the device. Some systems
* have getpagesize(2), though the linux man page has some odd cautions.
*/
#define DEFAULT_PAGE_SIZE 4096
/*
* Create an anonymous shared memory segment large enough to hold "length"
* bytes. The actual segment may be larger because mmap() operates on
* page boundaries (usually 4K).
*/
static void* sysCreateAnonShmem(size_t length)
{
void* ptr;
ptr = mmap(NULL, length, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANON, -1, 0);
if (ptr == MAP_FAILED) {
LOGW("mmap(%d, RW, SHARED|ANON) failed: %s\n", (int) length,
strerror(errno));
return NULL;
}
return ptr;
}
static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
{
off_t start, end;
size_t length;
assert(start_ != NULL);
assert(length_ != NULL);
start = lseek(fd, 0L, SEEK_CUR);
end = lseek(fd, 0L, SEEK_END);
(void) lseek(fd, start, SEEK_SET);
if (start == (off_t) -1 || end == (off_t) -1) {
LOGE("could not determine length of file\n");
return -1;
}
length = end - start;
if (length == 0) {
LOGE("file is empty\n");
return -1;
}
*start_ = start;
*length_ = length;
return 0;
}
/*
* Pull the contents of a file into an new shared memory segment. We grab
* everything from fd's current offset on.
*
* We need to know the length ahead of time so we can allocate a segment
* of sufficient size.
*/
int sysLoadFileInShmem(int fd, MemMapping* pMap)
{
off_t start;
size_t length, actual;
void* memPtr;
assert(pMap != NULL);
if (getFileStartAndLength(fd, &start, &length) < 0)
return -1;
memPtr = sysCreateAnonShmem(length);
if (memPtr == NULL)
return -1;
actual = read(fd, memPtr, length);
if (actual != length) {
LOGE("only read %d of %d bytes\n", (int) actual, (int) length);
sysReleaseShmem(pMap);
return -1;
}
pMap->baseAddr = pMap->addr = memPtr;
pMap->baseLength = pMap->length = length;
return 0;
}
/*
* Map a file (from fd's current offset) into a shared, read-only memory
* segment. The file offset must be a multiple of the page size.
*
* On success, returns 0 and fills out "pMap". On failure, returns a nonzero
* value and does not disturb "pMap".
*/
int sysMapFileInShmem(int fd, MemMapping* pMap)
{
off_t start;
size_t length;
void* memPtr;
assert(pMap != NULL);
if (getFileStartAndLength(fd, &start, &length) < 0)
return -1;
memPtr = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_SHARED, fd, start);
if (memPtr == MAP_FAILED) {
LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n", (int) length,
fd, (int) start, strerror(errno));
return -1;
}
pMap->baseAddr = pMap->addr = memPtr;
pMap->baseLength = pMap->length = length;
return 0;
}
/*
* Map part of a file (from fd's current offset) into a shared, read-only
* memory segment.
*
* On success, returns 0 and fills out "pMap". On failure, returns a nonzero
* value and does not disturb "pMap".
*/
int sysMapFileSegmentInShmem(int fd, off_t start, long length,
MemMapping* pMap)
{
off_t dummy;
size_t fileLength, actualLength;
off_t actualStart;
int adjust;
void* memPtr;
assert(pMap != NULL);
if (getFileStartAndLength(fd, &dummy, &fileLength) < 0)
return -1;
if (start + length > (long)fileLength) {
LOGW("bad segment: st=%d len=%ld flen=%d\n",
(int) start, length, (int) fileLength);
return -1;
}
/* adjust to be page-aligned */
adjust = start % DEFAULT_PAGE_SIZE;
actualStart = start - adjust;
actualLength = length + adjust;
memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
fd, actualStart);
if (memPtr == MAP_FAILED) {
LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n",
(int) actualLength, fd, (int) actualStart, strerror(errno));
return -1;
}
pMap->baseAddr = memPtr;
pMap->baseLength = actualLength;
pMap->addr = (char*)memPtr + adjust;
pMap->length = length;
LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d\n",
(int) start, (int) length,
pMap->baseAddr, (int) pMap->baseLength,
pMap->addr, (int) pMap->length);
return 0;
}
/*
* Release a memory mapping.
*/
void sysReleaseShmem(MemMapping* pMap)
{
if (pMap->baseAddr == NULL && pMap->baseLength == 0)
return;
if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
LOGW("munmap(%p, %d) failed: %s\n",
pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
} else {
LOGV("munmap(%p, %d) succeeded\n", pMap->baseAddr, pMap->baseLength);
pMap->baseAddr = NULL;
pMap->baseLength = 0;
}
}

61
minzip/SysUtil.h Normal file
View file

@ -0,0 +1,61 @@
/*
* Copyright 2006 The Android Open Source Project
*
* System utilities.
*/
#ifndef _MINZIP_SYSUTIL
#define _MINZIP_SYSUTIL
#include "inline_magic.h"
#include <sys/types.h>
/*
* Use this to keep track of mapped segments.
*/
typedef struct MemMapping {
void* addr; /* start of data */
size_t length; /* length of data */
void* baseAddr; /* page-aligned base address */
size_t baseLength; /* length of mapping */
} MemMapping;
/* copy a map */
INLINE void sysCopyMap(MemMapping* dst, const MemMapping* src) {
*dst = *src;
}
/*
* Load a file into a new shared memory segment. All data from the current
* offset to the end of the file is pulled in.
*
* The segment is read-write, allowing VM fixups. (It should be modified
* to support .gz/.zip compressed data.)
*
* On success, "pMap" is filled in, and zero is returned.
*/
int sysLoadFileInShmem(int fd, MemMapping* pMap);
/*
* Map a file (from fd's current offset) into a shared,
* read-only memory segment.
*
* On success, "pMap" is filled in, and zero is returned.
*/
int sysMapFileInShmem(int fd, MemMapping* pMap);
/*
* Like sysMapFileInShmem, but on only part of a file.
*/
int sysMapFileSegmentInShmem(int fd, off_t start, long length,
MemMapping* pMap);
/*
* Release the pages associated with a shared memory segment.
*
* This does not free "pMap"; it just releases the memory.
*/
void sysReleaseShmem(MemMapping* pMap);
#endif /*_MINZIP_SYSUTIL*/

1098
minzip/Zip.c Normal file

File diff suppressed because it is too large Load diff

206
minzip/Zip.h Normal file
View file

@ -0,0 +1,206 @@
/*
* Copyright 2006 The Android Open Source Project
*
* Simple Zip archive support.
*/
#ifndef _MINZIP_ZIP
#define _MINZIP_ZIP
#include "inline_magic.h"
#include <stdlib.h>
#include <utime.h>
#include "Hash.h"
#include "SysUtil.h"
/*
* One entry in the Zip archive. Treat this as opaque -- use accessors below.
*
* TODO: we're now keeping the pages mapped so we don't have to copy the
* filename. We can change the accessors to retrieve the various pieces
* directly from the source file instead of copying them out, for a very
* slight speed hit and a modest reduction in memory usage.
*/
typedef struct ZipEntry {
unsigned int fileNameLen;
const char* fileName; // not null-terminated
long offset;
long compLen;
long uncompLen;
int compression;
long modTime;
long crc32;
int versionMadeBy;
long externalFileAttributes;
} ZipEntry;
/*
* One Zip archive. Treat as opaque.
*/
typedef struct ZipArchive {
int fd;
unsigned int numEntries;
ZipEntry* pEntries;
HashTable* pHash; // maps file name to ZipEntry
MemMapping map;
} ZipArchive;
/*
* Represents a non-NUL-terminated string,
* which is how entry names are stored.
*/
typedef struct {
const char *str;
size_t len;
} UnterminatedString;
/*
* Open a Zip archive.
*
* On success, returns 0 and populates "pArchive". Returns nonzero errno
* value on failure.
*/
int mzOpenZipArchive(const char* fileName, ZipArchive* pArchive);
/*
* Close archive, releasing resources associated with it.
*
* Depending on the implementation this could unmap pages used by classes
* stored in a Jar. This should only be done after unloading classes.
*/
void mzCloseZipArchive(ZipArchive* pArchive);
/*
* Find an entry in the Zip archive, by name.
*/
const ZipEntry* mzFindZipEntry(const ZipArchive* pArchive,
const char* entryName);
/*
* Get the number of entries in the Zip archive.
*/
INLINE unsigned int mzZipEntryCount(const ZipArchive* pArchive) {
return pArchive->numEntries;
}
/*
* Get an entry by index. Returns NULL if the index is out-of-bounds.
*/
INLINE const ZipEntry*
mzGetZipEntryAt(const ZipArchive* pArchive, unsigned int index)
{
if (index < pArchive->numEntries) {
return pArchive->pEntries + index;
}
return NULL;
}
/*
* Get the index number of an entry in the archive.
*/
INLINE unsigned int
mzGetZipEntryIndex(const ZipArchive *pArchive, const ZipEntry *pEntry) {
return pEntry - pArchive->pEntries;
}
/*
* Simple accessors.
*/
INLINE UnterminatedString mzGetZipEntryFileName(const ZipEntry* pEntry) {
UnterminatedString ret;
ret.str = pEntry->fileName;
ret.len = pEntry->fileNameLen;
return ret;
}
INLINE long mzGetZipEntryOffset(const ZipEntry* pEntry) {
return pEntry->offset;
}
INLINE long mzGetZipEntryUncompLen(const ZipEntry* pEntry) {
return pEntry->uncompLen;
}
INLINE long mzGetZipEntryModTime(const ZipEntry* pEntry) {
return pEntry->modTime;
}
INLINE long mzGetZipEntryCrc32(const ZipEntry* pEntry) {
return pEntry->crc32;
}
bool mzIsZipEntrySymlink(const ZipEntry* pEntry);
/*
* Type definition for the callback function used by
* mzProcessZipEntryContents().
*/
typedef bool (*ProcessZipEntryContentsFunction)(const unsigned char *data,
int dataLen, void *cookie);
/*
* Stream the uncompressed data through the supplied function,
* passing cookie to it each time it gets called. processFunction
* may be called more than once.
*
* If processFunction returns false, the operation is abandoned and
* mzProcessZipEntryContents() immediately returns false.
*
* This is useful for calculating the hash of an entry's uncompressed contents.
*/
bool mzProcessZipEntryContents(const ZipArchive *pArchive,
const ZipEntry *pEntry, ProcessZipEntryContentsFunction processFunction,
void *cookie);
/*
* Read an entry into a buffer allocated by the caller.
*/
bool mzReadZipEntry(const ZipArchive* pArchive, const ZipEntry* pEntry,
char* buf, int bufLen);
/*
* Check the CRC on this entry; return true if it is correct.
* May do other internal checks as well.
*/
bool mzIsZipEntryIntact(const ZipArchive *pArchive, const ZipEntry *pEntry);
/*
* Inflate and write an entry to a file.
*/
bool mzExtractZipEntryToFile(const ZipArchive *pArchive,
const ZipEntry *pEntry, int fd);
/*
* Inflate all entries under zipDir to the directory specified by
* targetDir, which must exist and be a writable directory.
*
* The immediate children of zipDir will become the immediate
* children of targetDir; e.g., if the archive contains the entries
*
* a/b/c/one
* a/b/c/two
* a/b/c/d/three
*
* and mzExtractRecursive(a, "a/b/c", "/tmp", ...) is called, the resulting
* files will be
*
* /tmp/one
* /tmp/two
* /tmp/d/three
*
* flags is zero or more of the following:
*
* MZ_EXTRACT_FILES_ONLY - only unpack files, not directories or symlinks
* MZ_EXTRACT_DRY_RUN - don't do anything, but do invoke the callback
*
* If timestamp is non-NULL, file timestamps will be set accordingly.
*
* If callback is non-NULL, it will be invoked with each unpacked file.
*
* Returns true on success, false on failure.
*/
enum { MZ_EXTRACT_FILES_ONLY = 1, MZ_EXTRACT_DRY_RUN = 2 };
bool mzExtractRecursive(const ZipArchive *pArchive,
const char *zipDir, const char *targetDir,
int flags, const struct utimbuf *timestamp,
void (*callback)(const char *fn, void*), void *cookie);
#endif /*_MINZIP_ZIP*/

26
minzip/inline_magic.h Normal file
View file

@ -0,0 +1,26 @@
/*
* 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 MINZIP_INLINE_MAGIC_H_
#define MINZIP_INLINE_MAGIC_H_
#ifndef MINZIP_GENERATE_INLINES
#define INLINE extern __inline__
#else
#define INLINE
#endif
#endif // MINZIP_INLINE_MAGIC_H_

23
mtdutils/Android.mk Normal file
View file

@ -0,0 +1,23 @@
ifneq ($(TARGET_SIMULATOR),true)
ifeq ($(TARGET_ARCH),arm)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
mtdutils.c \
mounts.c
LOCAL_MODULE := libmtdutils
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := flash_image.c
LOCAL_MODULE := flash_image
LOCAL_STATIC_LIBRARIES := libmtdutils
LOCAL_SHARED_LIBRARIES := libcutils libc
include $(BUILD_EXECUTABLE)
endif # TARGET_ARCH == arm
endif # !TARGET_SIMULATOR

Some files were not shown because too many files have changed in this diff Show more