From 89d385c4d9a0580c9712b571eb3d3ff6b88f9b9a Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Thu, 4 Feb 2010 14:17:32 -0800 Subject: [PATCH 001/233] allow mouse/trackball click for enter on touch only devices. --- default_recovery_ui.c | 1 + 1 file changed, 1 insertion(+) diff --git a/default_recovery_ui.c b/default_recovery_ui.c index d4e6204..a531d7e 100644 --- a/default_recovery_ui.c +++ b/default_recovery_ui.c @@ -49,6 +49,7 @@ int device_handle_key(int key_code, int visible) { return HIGHLIGHT_UP; case KEY_ENTER: + case BTN_MOUSE: return SELECT_ITEM; } } From 0d4ff2fcc6162c5996cca39349662d378c7c26c3 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Mon, 8 Feb 2010 15:05:21 -0800 Subject: [PATCH 002/233] allow alt-l or menu to bring up the recovery menu --- default_recovery_ui.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/default_recovery_ui.c b/default_recovery_ui.c index a531d7e..cb76b9a 100644 --- a/default_recovery_ui.c +++ b/default_recovery_ui.c @@ -30,7 +30,10 @@ char* MENU_ITEMS[] = { "reboot system now", NULL }; int device_toggle_display(volatile char* key_pressed, int key_code) { - return key_code == KEY_HOME; + int alt = key_pressed[KEY_LEFTALT] || key_pressed[KEY_RIGHTALT]; + if (alt && key_code == KEY_L) + return 1; + return key_code == KEY_HOME || key_code == KEY_MENU; } int device_reboot_now(volatile char* key_pressed, int key_code) { From 4c1eed25738e2529ad5f785ca7eff29fa6d4ff4d Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Thu, 11 Feb 2010 18:59:58 -0800 Subject: [PATCH 003/233] Initial reintegration of legacy update process that used update-script --- Android.mk | 3 + amend/Android.mk | 53 ++ amend/amend.c | 32 ++ amend/amend.h | 25 + amend/ast.c | 198 +++++++ amend/ast.h | 165 ++++++ amend/commands.c | 273 +++++++++ amend/commands.h | 96 ++++ amend/execute.c | 315 ++++++++++ amend/execute.h | 25 + amend/lexer.h | 43 ++ amend/lexer.l | 299 ++++++++++ amend/main.c | 195 +++++++ amend/parser.h | 24 + amend/parser_y.y | 430 ++++++++++++++ amend/permissions.c | 270 +++++++++ amend/permissions.h | 111 ++++ amend/register.c | 394 +++++++++++++ amend/register.h | 23 + amend/symtab.c | 132 +++++ amend/symtab.h | 34 ++ amend/test_commands.c | 538 ++++++++++++++++++ amend/test_permissions.c | 347 +++++++++++ amend/test_symtab.c | 146 +++++ amend/tests/001-nop/expected.txt | 1 + amend/tests/001-nop/info.txt | 2 + amend/tests/001-nop/run | 17 + amend/tests/002-lex-empty/SKIP | 0 amend/tests/002-lex-empty/expected.txt | 1 + amend/tests/002-lex-empty/info.txt | 1 + amend/tests/002-lex-empty/input | 0 amend/tests/002-lex-empty/run | 17 + amend/tests/003-lex-command/expected.txt | 13 + amend/tests/003-lex-command/info.txt | 1 + amend/tests/003-lex-command/input | 6 + amend/tests/003-lex-command/input2 | 1 + amend/tests/003-lex-command/input3 | 1 + amend/tests/003-lex-command/input4 | 1 + amend/tests/003-lex-command/run | 20 + amend/tests/004-lex-comment/expected.txt | 5 + amend/tests/004-lex-comment/info.txt | 1 + amend/tests/004-lex-comment/input | 4 + amend/tests/004-lex-comment/run | 17 + .../tests/005-lex-quoted-string/expected.txt | 13 + amend/tests/005-lex-quoted-string/info.txt | 1 + amend/tests/005-lex-quoted-string/input | 5 + amend/tests/005-lex-quoted-string/input2 | 2 + amend/tests/005-lex-quoted-string/input3 | 2 + amend/tests/005-lex-quoted-string/run | 19 + amend/tests/006-lex-words/SKIP | 0 amend/tests/006-lex-words/expected.txt | 6 + amend/tests/006-lex-words/info.txt | 1 + amend/tests/006-lex-words/input | 5 + amend/tests/006-lex-words/input2 | 2 + amend/tests/006-lex-words/input3 | 2 + amend/tests/006-lex-words/run | 17 + amend/tests/007-lex-real-script/expected.txt | 11 + amend/tests/007-lex-real-script/info.txt | 1 + amend/tests/007-lex-real-script/input | 10 + amend/tests/007-lex-real-script/run | 17 + .../tests/008-parse-real-script/expected.txt | 74 +++ amend/tests/008-parse-real-script/info.txt | 1 + amend/tests/008-parse-real-script/input | 10 + amend/tests/008-parse-real-script/run | 17 + amend/tests/XXX-long-token/SKIP | 0 amend/tests/XXX-stack-overflow/SKIP | 0 amend/tests/one-test | 150 +++++ amend/tests/run-all-tests | 69 +++ install.c | 9 +- install.h | 2 +- legacy.c | 98 ++++ legacy.h | 5 + 72 files changed, 4827 insertions(+), 2 deletions(-) create mode 100644 amend/Android.mk create mode 100644 amend/amend.c create mode 100644 amend/amend.h create mode 100644 amend/ast.c create mode 100644 amend/ast.h create mode 100644 amend/commands.c create mode 100644 amend/commands.h create mode 100644 amend/execute.c create mode 100644 amend/execute.h create mode 100644 amend/lexer.h create mode 100644 amend/lexer.l create mode 100644 amend/main.c create mode 100644 amend/parser.h create mode 100644 amend/parser_y.y create mode 100644 amend/permissions.c create mode 100644 amend/permissions.h create mode 100644 amend/register.c create mode 100644 amend/register.h create mode 100644 amend/symtab.c create mode 100644 amend/symtab.h create mode 100644 amend/test_commands.c create mode 100644 amend/test_permissions.c create mode 100644 amend/test_symtab.c create mode 100644 amend/tests/001-nop/expected.txt create mode 100644 amend/tests/001-nop/info.txt create mode 100644 amend/tests/001-nop/run create mode 100644 amend/tests/002-lex-empty/SKIP create mode 100644 amend/tests/002-lex-empty/expected.txt create mode 100644 amend/tests/002-lex-empty/info.txt create mode 100644 amend/tests/002-lex-empty/input create mode 100644 amend/tests/002-lex-empty/run create mode 100644 amend/tests/003-lex-command/expected.txt create mode 100644 amend/tests/003-lex-command/info.txt create mode 100644 amend/tests/003-lex-command/input create mode 100644 amend/tests/003-lex-command/input2 create mode 100644 amend/tests/003-lex-command/input3 create mode 100644 amend/tests/003-lex-command/input4 create mode 100644 amend/tests/003-lex-command/run create mode 100644 amend/tests/004-lex-comment/expected.txt create mode 100644 amend/tests/004-lex-comment/info.txt create mode 100644 amend/tests/004-lex-comment/input create mode 100644 amend/tests/004-lex-comment/run create mode 100644 amend/tests/005-lex-quoted-string/expected.txt create mode 100644 amend/tests/005-lex-quoted-string/info.txt create mode 100644 amend/tests/005-lex-quoted-string/input create mode 100644 amend/tests/005-lex-quoted-string/input2 create mode 100644 amend/tests/005-lex-quoted-string/input3 create mode 100644 amend/tests/005-lex-quoted-string/run create mode 100644 amend/tests/006-lex-words/SKIP create mode 100644 amend/tests/006-lex-words/expected.txt create mode 100644 amend/tests/006-lex-words/info.txt create mode 100644 amend/tests/006-lex-words/input create mode 100644 amend/tests/006-lex-words/input2 create mode 100644 amend/tests/006-lex-words/input3 create mode 100644 amend/tests/006-lex-words/run create mode 100644 amend/tests/007-lex-real-script/expected.txt create mode 100644 amend/tests/007-lex-real-script/info.txt create mode 100644 amend/tests/007-lex-real-script/input create mode 100644 amend/tests/007-lex-real-script/run create mode 100644 amend/tests/008-parse-real-script/expected.txt create mode 100644 amend/tests/008-parse-real-script/info.txt create mode 100644 amend/tests/008-parse-real-script/input create mode 100644 amend/tests/008-parse-real-script/run create mode 100644 amend/tests/XXX-long-token/SKIP create mode 100644 amend/tests/XXX-stack-overflow/SKIP create mode 100755 amend/tests/one-test create mode 100755 amend/tests/run-all-tests create mode 100644 legacy.c create mode 100644 legacy.h diff --git a/Android.mk b/Android.mk index deec80a..6a64d86 100644 --- a/Android.mk +++ b/Android.mk @@ -7,6 +7,7 @@ include $(CLEAR_VARS) commands_recovery_local_path := $(LOCAL_PATH) LOCAL_SRC_FILES := \ + legacy.c \ recovery.c \ bootloader.c \ firmware.c \ @@ -40,9 +41,11 @@ endif LOCAL_STATIC_LIBRARIES += libminzip libunz libmtdutils libmincrypt LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils LOCAL_STATIC_LIBRARIES += libstdc++ libc +LOCAL_STATIC_LIBRARIES += libamend include $(BUILD_EXECUTABLE) +include $(commands_recovery_local_path)/amend/Android.mk include $(commands_recovery_local_path)/minui/Android.mk include $(commands_recovery_local_path)/minzip/Android.mk include $(commands_recovery_local_path)/mtdutils/Android.mk diff --git a/amend/Android.mk b/amend/Android.mk new file mode 100644 index 0000000..ae2d44a --- /dev/null +++ b/amend/Android.mk @@ -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) diff --git a/amend/amend.c b/amend/amend.c new file mode 100644 index 0000000..49cd64e --- /dev/null +++ b/amend/amend.c @@ -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 +#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; +} diff --git a/amend/amend.h b/amend/amend.h new file mode 100644 index 0000000..416f974 --- /dev/null +++ b/amend/amend.h @@ -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_ diff --git a/amend/ast.c b/amend/ast.c new file mode 100644 index 0000000..f53efdc --- /dev/null +++ b/amend/ast.c @@ -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 +#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\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\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]); + } +} diff --git a/amend/ast.h b/amend/ast.h new file mode 100644 index 0000000..7834a2b --- /dev/null +++ b/amend/ast.h @@ -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; + + +/* ::= + * | + * + */ +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; +}; + + +/* ::= + * + */ +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; + + +/* ::= + * ! | + * + */ +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; + + +/* ::= + * | + * + */ +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_ diff --git a/amend/commands.c b/amend/commands.c new file mode 100644 index 0000000..75ff828 --- /dev/null +++ b/amend/commands.c @@ -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 +#include +#include +#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; +} diff --git a/amend/commands.h b/amend/commands.h new file mode 100644 index 0000000..38931c0 --- /dev/null +++ b/amend/commands.h @@ -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_ diff --git a/amend/execute.c b/amend/execute.c new file mode 100644 index 0000000..9162ad6 --- /dev/null +++ b/amend/execute.c @@ -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 +#include +#include +#undef NDEBUG +#include +#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; +} diff --git a/amend/execute.h b/amend/execute.h new file mode 100644 index 0000000..3becb48 --- /dev/null +++ b/amend/execute.h @@ -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_ diff --git a/amend/lexer.h b/amend/lexer.h new file mode 100644 index 0000000..fc716fd --- /dev/null +++ b/amend/lexer.h @@ -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 +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_ diff --git a/amend/lexer.l b/amend/lexer.l new file mode 100644 index 0000000..80896d1 --- /dev/null +++ b/amend/lexer.l @@ -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 + #include + #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 */ +{ + {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; + } + } + +{ + {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 */ + +\" { + /* Initial quote */ + gStr.nextc = gStr.value; + BEGIN(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; + } + } + + <> | + \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++); + } + } + } + +{ + /*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; + } + } + +{ + \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; +} diff --git a/amend/main.c b/amend/main.c new file mode 100644 index 0000000..9bb0785 --- /dev/null +++ b/amend/main.c @@ -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 +#include +#include +#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] []\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; +} diff --git a/amend/parser.h b/amend/parser.h new file mode 100644 index 0000000..aeb8657 --- /dev/null +++ b/amend/parser.h @@ -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_ diff --git a/amend/parser_y.y b/amend/parser_y.y new file mode 100644 index 0000000..b634016 --- /dev/null +++ b/amend/parser_y.y @@ -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 + #include + #include + #include + #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 TOK_STRING TOK_IDENTIFIER TOK_WORD + +%type lines +%type command line +%type function_arguments +%type function_arguments_or_empty +%type function_call +%type function_name +%type string_value +%type boolean_expression +%type word_list +%type 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 */ diff --git a/amend/permissions.c b/amend/permissions.c new file mode 100644 index 0000000..a642d0b --- /dev/null +++ b/amend/permissions.c @@ -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 +#include +#include "permissions.h" + +int +initPermissionRequestList(PermissionRequestList *list) +{ + if (list != NULL) { + list->requests = NULL; + list->numRequests = 0; + list->requestsAllocated = 0; + return 0; + } + return -1; +} + +int +addPermissionRequestToList(PermissionRequestList *list, + const char *path, bool recursive, unsigned int permissions) +{ + if (list == NULL || list->numRequests < 0 || + list->requestsAllocated < list->numRequests || path == NULL) + { + return -1; + } + + if (list->numRequests == list->requestsAllocated) { + int newSize; + PermissionRequest *newRequests; + + newSize = list->requestsAllocated * 2; + if (newSize < 16) { + newSize = 16; + } + newRequests = (PermissionRequest *)realloc(list->requests, + newSize * sizeof(PermissionRequest)); + if (newRequests == NULL) { + return -2; + } + list->requests = newRequests; + list->requestsAllocated = newSize; + } + + PermissionRequest *req; + req = &list->requests[list->numRequests++]; + req->path = strdup(path); + if (req->path == NULL) { + list->numRequests--; + return -3; + } + req->recursive = recursive; + req->requested = permissions; + req->allowed = 0; + + return 0; +} + +void +freePermissionRequestListElements(PermissionRequestList *list) +{ + if (list != NULL && list->numRequests >= 0 && + list->requestsAllocated >= list->numRequests) + { + int i; + for (i = 0; i < list->numRequests; i++) { + free((void *)list->requests[i].path); + } + free(list->requests); + initPermissionRequestList(list); + } +} + +/* + * Global permission table + */ + +static struct { + Permission *permissions; + int numPermissionEntries; + int allocatedPermissionEntries; + bool permissionStateInitialized; +} gPermissionState = { +#if 1 + NULL, 0, 0, false +#else + .permissions = NULL, + .numPermissionEntries = 0, + .allocatedPermissionEntries = 0, + .permissionStateInitialized = false +#endif +}; + +int +permissionInit() +{ + if (gPermissionState.permissionStateInitialized) { + return -1; + } + gPermissionState.permissions = NULL; + gPermissionState.numPermissionEntries = 0; + gPermissionState.allocatedPermissionEntries = 0; + gPermissionState.permissionStateInitialized = true; +//xxx maybe add an "namespace root gets no permissions" fallback by default + return 0; +} + +void +permissionCleanup() +{ + if (gPermissionState.permissionStateInitialized) { + gPermissionState.permissionStateInitialized = false; + if (gPermissionState.permissions != NULL) { + int i; + for (i = 0; i < gPermissionState.numPermissionEntries; i++) { + free((void *)gPermissionState.permissions[i].path); + } + free(gPermissionState.permissions); + } + } +} + +int +getPermissionCount() +{ + if (gPermissionState.permissionStateInitialized) { + return gPermissionState.numPermissionEntries; + } + return -1; +} + +const Permission * +getPermissionAt(int index) +{ + if (!gPermissionState.permissionStateInitialized) { + return NULL; + } + if (index < 0 || index >= gPermissionState.numPermissionEntries) { + return NULL; + } + return &gPermissionState.permissions[index]; +} + +int +getAllowedPermissions(const char *path, bool recursive, + unsigned int *outAllowed) +{ + if (!gPermissionState.permissionStateInitialized) { + return -2; + } + if (outAllowed == NULL) { + return -1; + } + *outAllowed = 0; + if (path == NULL) { + return -1; + } + //TODO: implement this for real. + recursive = false; + *outAllowed = PERMSET_ALL; + return 0; +} + +int +countPermissionConflicts(PermissionRequestList *requests, bool updateAllowed) +{ + if (!gPermissionState.permissionStateInitialized) { + return -2; + } + if (requests == NULL || requests->requests == NULL || + requests->numRequests < 0 || + requests->requestsAllocated < requests->numRequests) + { + return -1; + } + int conflicts = 0; + int i; + for (i = 0; i < requests->numRequests; i++) { + PermissionRequest *req; + unsigned int allowed; + int ret; + + req = &requests->requests[i]; + ret = getAllowedPermissions(req->path, req->recursive, &allowed); + if (ret < 0) { + return ret; + } + if ((req->requested & ~allowed) != 0) { + conflicts++; + } + if (updateAllowed) { + req->allowed = allowed; + } + } + return conflicts; +} + +int +registerPermissionSet(int count, Permission *set) +{ + if (!gPermissionState.permissionStateInitialized) { + return -2; + } + if (count < 0 || (count > 0 && set == NULL)) { + return -1; + } + if (count == 0) { + return 0; + } + + if (gPermissionState.numPermissionEntries + count >= + gPermissionState.allocatedPermissionEntries) + { + Permission *newList; + int newSize; + + newSize = (gPermissionState.allocatedPermissionEntries + count) * 2; + if (newSize < 16) { + newSize = 16; + } + newList = (Permission *)realloc(gPermissionState.permissions, + newSize * sizeof(Permission)); + if (newList == NULL) { + return -3; + } + gPermissionState.permissions = newList; + gPermissionState.allocatedPermissionEntries = newSize; + } + + Permission *p = &gPermissionState.permissions[ + gPermissionState.numPermissionEntries]; + int i; + for (i = 0; i < count; i++) { + *p = set[i]; + //TODO: cache the strlen of the path + //TODO: normalize; strip off trailing / + p->path = strdup(p->path); + if (p->path == NULL) { + /* If we can't add all of the entries, we don't + * add any of them. + */ + Permission *pp = &gPermissionState.permissions[ + gPermissionState.numPermissionEntries]; + while (pp != p) { + free((void *)pp->path); + pp++; + } + return -4; + } + p++; + } + gPermissionState.numPermissionEntries += count; + + return 0; +} diff --git a/amend/permissions.h b/amend/permissions.h new file mode 100644 index 0000000..5b1d14d --- /dev/null +++ b/amend/permissions.h @@ -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 + +#define PERM_NONE (0) +#define PERM_STAT (1<<0) +#define PERM_READ (1<<1) +#define PERM_WRITE (1<<2) // including create, delete, mkdir, rmdir +#define PERM_CHMOD (1<<3) +#define PERM_CHOWN (1<<4) +#define PERM_CHGRP (1<<5) +#define PERM_SETUID (1<<6) +#define PERM_SETGID (1<<7) + +#define PERMSET_READ (PERM_STAT | PERM_READ) +#define PERMSET_WRITE (PERMSET_READ | PERM_WRITE) + +#define PERMSET_ALL \ + (PERM_STAT | PERM_READ | PERM_WRITE | PERM_CHMOD | \ + PERM_CHOWN | PERM_CHGRP | PERM_SETUID | PERM_SETGID) + +typedef struct { + unsigned int requested; + unsigned int allowed; + const char *path; + bool recursive; +} PermissionRequest; + +typedef struct { + PermissionRequest *requests; + int numRequests; + int requestsAllocated; +} PermissionRequestList; + +/* Properly clear out a PermissionRequestList. + * + * @return 0 if list is non-NULL, negative otherwise. + */ +int initPermissionRequestList(PermissionRequestList *list); + +/* Add a permission request to the list, allocating more space + * if necessary. + * + * @return 0 on success or a negative value on failure. + */ +int addPermissionRequestToList(PermissionRequestList *list, + const char *path, bool recursive, unsigned int permissions); + +/* Free anything allocated by addPermissionRequestToList(). The caller + * is responsible for freeing the actual PermissionRequestList. + */ +void freePermissionRequestListElements(PermissionRequestList *list); + + +/* + * Global permission table + */ + +typedef struct { + const char *path; + unsigned int allowed; +} Permission; + +int permissionInit(void); +void permissionCleanup(void); + +/* Returns the allowed permissions for the path in "outAllowed". + * Returns 0 if successful, negative if a parameter or global state + * is bad. + */ +int getAllowedPermissions(const char *path, bool recursive, + unsigned int *outAllowed); + +/* More-recently-registered permissions override older permissions. + */ +int registerPermissionSet(int count, Permission *set); + +/* Check to make sure that each request is allowed. + * + * @param requests The list of permission requests + * @param updateAllowed If true, update the "allowed" field in each + * element of the list + * @return the number of requests that were denied, or negative if + * an error occurred. + */ +int countPermissionConflicts(PermissionRequestList *requests, + bool updateAllowed); + +/* Inspection/testing/debugging functions + */ +int getPermissionCount(void); +const Permission *getPermissionAt(int index); + +#endif // AMEND_PERMISSIONS_H_ diff --git a/amend/register.c b/amend/register.c new file mode 100644 index 0000000..167dd32 --- /dev/null +++ b/amend/register.c @@ -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 +#include +#include +#undef NDEBUG +#include +#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 + */ +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 + */ +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 + */ +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 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() + * + * 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() + */ +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(, [, ...]) + * If matches (strcmp) any of ..., returns , + * 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(, [, ...]) + * 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; +} diff --git a/amend/register.h b/amend/register.h new file mode 100644 index 0000000..1d9eacb --- /dev/null +++ b/amend/register.h @@ -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_ diff --git a/amend/symtab.c b/amend/symtab.c new file mode 100644 index 0000000..835d2fc --- /dev/null +++ b/amend/symtab.c @@ -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 +#include +#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; +} diff --git a/amend/symtab.h b/amend/symtab.h new file mode 100644 index 0000000..f83c65b --- /dev/null +++ b/amend/symtab.h @@ -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_ diff --git a/amend/test_commands.c b/amend/test_commands.c new file mode 100644 index 0000000..be938ac --- /dev/null +++ b/amend/test_commands.c @@ -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 +#include +#include +#undef NDEBUG +#include +#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; +} diff --git a/amend/test_permissions.c b/amend/test_permissions.c new file mode 100644 index 0000000..c389456 --- /dev/null +++ b/amend/test_permissions.c @@ -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 +#include +#include +#undef NDEBUG +#include +#include "permissions.h" + +static int +test_permission_list() +{ + PermissionRequestList list; + int ret; + int numRequests; + + /* Bad parameter + */ + ret = initPermissionRequestList(NULL); + assert(ret < 0); + + /* Good parameter + */ + ret = initPermissionRequestList(&list); + assert(ret == 0); + + /* Bad parameters + */ + ret = addPermissionRequestToList(NULL, NULL, false, 0); + assert(ret < 0); + + ret = addPermissionRequestToList(&list, NULL, false, 0); + assert(ret < 0); + + /* Good parameters + */ + numRequests = 0; + + ret = addPermissionRequestToList(&list, "one", false, 1); + assert(ret == 0); + numRequests++; + + ret = addPermissionRequestToList(&list, "two", false, 2); + assert(ret == 0); + numRequests++; + + ret = addPermissionRequestToList(&list, "three", false, 3); + assert(ret == 0); + numRequests++; + + ret = addPermissionRequestToList(&list, "recursive", true, 55); + assert(ret == 0); + numRequests++; + + /* Validate the list + */ + assert(list.requests != NULL); + assert(list.numRequests == numRequests); + assert(list.numRequests <= list.requestsAllocated); + bool sawOne = false; + bool sawTwo = false; + bool sawThree = false; + bool sawRecursive = false; + int i; + for (i = 0; i < list.numRequests; i++) { + PermissionRequest *req = &list.requests[i]; + assert(req->allowed == 0); + + /* Order isn't guaranteed, so we have to switch every time. + */ + if (strcmp(req->path, "one") == 0) { + assert(!sawOne); + assert(req->requested == 1); + assert(!req->recursive); + sawOne = true; + } else if (strcmp(req->path, "two") == 0) { + assert(!sawTwo); + assert(req->requested == 2); + assert(!req->recursive); + sawTwo = true; + } else if (strcmp(req->path, "three") == 0) { + assert(!sawThree); + assert(req->requested == 3); + assert(!req->recursive); + sawThree = true; + } else if (strcmp(req->path, "recursive") == 0) { + assert(!sawRecursive); + assert(req->requested == 55); + assert(req->recursive); + sawRecursive = true; + } else { + assert(false); + } + } + assert(sawOne); + assert(sawTwo); + assert(sawThree); + assert(sawRecursive); + + /* Smoke test the teardown + */ + freePermissionRequestListElements(&list); + + return 0; +} + +static int +test_permission_table() +{ + int ret; + + /* Test the global permissions table. + * Try calling functions without initializing first. + */ + ret = registerPermissionSet(0, NULL); + assert(ret < 0); + + ret = countPermissionConflicts((PermissionRequestList *)16, false); + assert(ret < 0); + + ret = getPermissionCount(); + assert(ret < 0); + + const Permission *p; + p = getPermissionAt(0); + assert(p == NULL); + + /* Initialize. + */ + ret = permissionInit(); + assert(ret == 0); + + /* Make sure we can't initialize twice. + */ + ret = permissionInit(); + assert(ret < 0); + + /* Test the inspection functions. + */ + ret = getPermissionCount(); + assert(ret == 0); + + p = getPermissionAt(-1); + assert(p == NULL); + + p = getPermissionAt(0); + assert(p == NULL); + + p = getPermissionAt(1); + assert(p == NULL); + + /* Test registerPermissionSet(). + * Try some bad parameter values. + */ + ret = registerPermissionSet(-1, NULL); + assert(ret < 0); + + ret = registerPermissionSet(1, NULL); + assert(ret < 0); + + /* Register some permissions. + */ + Permission p1; + p1.path = "one"; + p1.allowed = 1; + ret = registerPermissionSet(1, &p1); + assert(ret == 0); + ret = getPermissionCount(); + assert(ret == 1); + + Permission p2[2]; + p2[0].path = "two"; + p2[0].allowed = 2; + p2[1].path = "three"; + p2[1].allowed = 3; + ret = registerPermissionSet(2, p2); + assert(ret == 0); + ret = getPermissionCount(); + assert(ret == 3); + + ret = registerPermissionSet(0, NULL); + assert(ret == 0); + ret = getPermissionCount(); + assert(ret == 3); + + p1.path = "four"; + p1.allowed = 4; + ret = registerPermissionSet(1, &p1); + assert(ret == 0); + + /* Make sure the table looks correct. + * Order is important; more-recent additions + * should appear at higher indices. + */ + ret = getPermissionCount(); + assert(ret == 4); + + int i; + for (i = 0; i < ret; i++) { + const Permission *p; + p = getPermissionAt(i); + assert(p != NULL); + assert(p->allowed == (unsigned int)(i + 1)); + switch (i) { + case 0: + assert(strcmp(p->path, "one") == 0); + break; + case 1: + assert(strcmp(p->path, "two") == 0); + break; + case 2: + assert(strcmp(p->path, "three") == 0); + break; + case 3: + assert(strcmp(p->path, "four") == 0); + break; + default: + assert(!"internal error"); + break; + } + } + p = getPermissionAt(ret); + assert(p == NULL); + + /* Smoke test the teardown + */ + permissionCleanup(); + + return 0; +} + +static int +test_allowed_permissions() +{ + int ret; + int numPerms; + + /* Make sure these fail before initialization. + */ + ret = countPermissionConflicts((PermissionRequestList *)1, false); + assert(ret < 0); + + ret = getAllowedPermissions((const char *)1, false, (unsigned int *)1); + assert(ret < 0); + + /* Initialize. + */ + ret = permissionInit(); + assert(ret == 0); + + /* Make sure countPermissionConflicts() fails with bad parameters. + */ + ret = countPermissionConflicts(NULL, false); + assert(ret < 0); + + /* Register a set of permissions. + */ + Permission perms[] = { + { "/", PERM_NONE }, + { "/stat", PERM_STAT }, + { "/read", PERMSET_READ }, + { "/write", PERMSET_WRITE }, + { "/.stat", PERM_STAT }, + { "/.stat/.read", PERMSET_READ }, + { "/.stat/.read/.write", PERMSET_WRITE }, + { "/.stat/.write", PERMSET_WRITE }, + }; + numPerms = sizeof(perms) / sizeof(perms[0]); + ret = registerPermissionSet(numPerms, perms); + assert(ret == 0); + + /* Build a permission request list. + */ + PermissionRequestList list; + ret = initPermissionRequestList(&list); + assert(ret == 0); + + ret = addPermissionRequestToList(&list, "/stat", false, PERM_STAT); + assert(ret == 0); + + ret = addPermissionRequestToList(&list, "/read", false, PERM_READ); + assert(ret == 0); + + ret = addPermissionRequestToList(&list, "/write", false, PERM_WRITE); + assert(ret == 0); + + //TODO: cover more cases once the permission stuff has been implemented + + /* All of the requests in the list should be allowed. + */ + ret = countPermissionConflicts(&list, false); + assert(ret == 0); + + /* Add a request that will be denied. + */ + ret = addPermissionRequestToList(&list, "/stat", false, 1<<31 | PERM_STAT); + assert(ret == 0); + + ret = countPermissionConflicts(&list, false); + assert(ret == 1); + + //TODO: more tests + + permissionCleanup(); + + return 0; +} + +int +test_permissions() +{ + int ret; + + ret = test_permission_list(); + if (ret != 0) { + fprintf(stderr, "test_permission_list() failed: %d\n", ret); + return ret; + } + + ret = test_permission_table(); + if (ret != 0) { + fprintf(stderr, "test_permission_table() failed: %d\n", ret); + return ret; + } + + ret = test_allowed_permissions(); + if (ret != 0) { + fprintf(stderr, "test_permission_table() failed: %d\n", ret); + return ret; + } + + return 0; +} diff --git a/amend/test_symtab.c b/amend/test_symtab.c new file mode 100644 index 0000000..017d18c --- /dev/null +++ b/amend/test_symtab.c @@ -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 +#undef NDEBUG +#include +#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; +} diff --git a/amend/tests/001-nop/expected.txt b/amend/tests/001-nop/expected.txt new file mode 100644 index 0000000..d4a85ce --- /dev/null +++ b/amend/tests/001-nop/expected.txt @@ -0,0 +1 @@ +I am a jelly donut. diff --git a/amend/tests/001-nop/info.txt b/amend/tests/001-nop/info.txt new file mode 100644 index 0000000..9942f10 --- /dev/null +++ b/amend/tests/001-nop/info.txt @@ -0,0 +1,2 @@ +This is a sample no-op test, which does at least serve to verify that the +test harness is working. diff --git a/amend/tests/001-nop/run b/amend/tests/001-nop/run new file mode 100644 index 0000000..51637c1 --- /dev/null +++ b/amend/tests/001-nop/run @@ -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.' diff --git a/amend/tests/002-lex-empty/SKIP b/amend/tests/002-lex-empty/SKIP new file mode 100644 index 0000000..e69de29 diff --git a/amend/tests/002-lex-empty/expected.txt b/amend/tests/002-lex-empty/expected.txt new file mode 100644 index 0000000..822a54c --- /dev/null +++ b/amend/tests/002-lex-empty/expected.txt @@ -0,0 +1 @@ + EOF diff --git a/amend/tests/002-lex-empty/info.txt b/amend/tests/002-lex-empty/info.txt new file mode 100644 index 0000000..090083f --- /dev/null +++ b/amend/tests/002-lex-empty/info.txt @@ -0,0 +1 @@ +Test to make sure that an empty file is accepted properly. diff --git a/amend/tests/002-lex-empty/input b/amend/tests/002-lex-empty/input new file mode 100644 index 0000000..e69de29 diff --git a/amend/tests/002-lex-empty/run b/amend/tests/002-lex-empty/run new file mode 100644 index 0000000..35c4a4f --- /dev/null +++ b/amend/tests/002-lex-empty/run @@ -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 diff --git a/amend/tests/003-lex-command/expected.txt b/amend/tests/003-lex-command/expected.txt new file mode 100644 index 0000000..e40db0c --- /dev/null +++ b/amend/tests/003-lex-command/expected.txt @@ -0,0 +1,13 @@ + IDENTIFIER EOL + IDENTIFIER EOL + IDENTIFIER EOL + IDENTIFIER EOL + 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 diff --git a/amend/tests/003-lex-command/info.txt b/amend/tests/003-lex-command/info.txt new file mode 100644 index 0000000..9296648 --- /dev/null +++ b/amend/tests/003-lex-command/info.txt @@ -0,0 +1 @@ +Test to make sure that simple command names are tokenized properly. diff --git a/amend/tests/003-lex-command/input b/amend/tests/003-lex-command/input new file mode 100644 index 0000000..b9ef231 --- /dev/null +++ b/amend/tests/003-lex-command/input @@ -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 diff --git a/amend/tests/003-lex-command/input2 b/amend/tests/003-lex-command/input2 new file mode 100644 index 0000000..eb5daf7 --- /dev/null +++ b/amend/tests/003-lex-command/input2 @@ -0,0 +1 @@ +"quoted" diff --git a/amend/tests/003-lex-command/input3 b/amend/tests/003-lex-command/input3 new file mode 100644 index 0000000..f1c8738 --- /dev/null +++ b/amend/tests/003-lex-command/input3 @@ -0,0 +1 @@ +== diff --git a/amend/tests/003-lex-command/input4 b/amend/tests/003-lex-command/input4 new file mode 100644 index 0000000..3ad5abd --- /dev/null +++ b/amend/tests/003-lex-command/input4 @@ -0,0 +1 @@ +99 diff --git a/amend/tests/003-lex-command/run b/amend/tests/003-lex-command/run new file mode 100644 index 0000000..2e21fab --- /dev/null +++ b/amend/tests/003-lex-command/run @@ -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 diff --git a/amend/tests/004-lex-comment/expected.txt b/amend/tests/004-lex-comment/expected.txt new file mode 100644 index 0000000..a728a5e --- /dev/null +++ b/amend/tests/004-lex-comment/expected.txt @@ -0,0 +1,5 @@ + IDENTIFIER EOL + IDENTIFIER EOL + EOL + EOL + EOF diff --git a/amend/tests/004-lex-comment/info.txt b/amend/tests/004-lex-comment/info.txt new file mode 100644 index 0000000..0691248 --- /dev/null +++ b/amend/tests/004-lex-comment/info.txt @@ -0,0 +1 @@ +Test to make sure that comments are stripped out. diff --git a/amend/tests/004-lex-comment/input b/amend/tests/004-lex-comment/input new file mode 100644 index 0000000..6736c95 --- /dev/null +++ b/amend/tests/004-lex-comment/input @@ -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 diff --git a/amend/tests/004-lex-comment/run b/amend/tests/004-lex-comment/run new file mode 100644 index 0000000..35c4a4f --- /dev/null +++ b/amend/tests/004-lex-comment/run @@ -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 diff --git a/amend/tests/005-lex-quoted-string/expected.txt b/amend/tests/005-lex-quoted-string/expected.txt new file mode 100644 index 0000000..9bb5ac4 --- /dev/null +++ b/amend/tests/005-lex-quoted-string/expected.txt @@ -0,0 +1,13 @@ + IDENTIFIER WORD EOL + IDENTIFIER WORD EOL + IDENTIFIER WORD EOL + IDENTIFIER WORD EOL + IDENTIFIER WORD EOL + EOF + EOL + IDENTIFIERline 2: unterminated string at ' +' + ??? <0> + EOL + IDENTIFIERline 2: illegal escape at '\n' + ??? <0> diff --git a/amend/tests/005-lex-quoted-string/info.txt b/amend/tests/005-lex-quoted-string/info.txt new file mode 100644 index 0000000..be458bd --- /dev/null +++ b/amend/tests/005-lex-quoted-string/info.txt @@ -0,0 +1 @@ +Test to make sure that quoted strings are tokenized properly. diff --git a/amend/tests/005-lex-quoted-string/input b/amend/tests/005-lex-quoted-string/input new file mode 100644 index 0000000..2b34bbc --- /dev/null +++ b/amend/tests/005-lex-quoted-string/input @@ -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" diff --git a/amend/tests/005-lex-quoted-string/input2 b/amend/tests/005-lex-quoted-string/input2 new file mode 100644 index 0000000..09e6689 --- /dev/null +++ b/amend/tests/005-lex-quoted-string/input2 @@ -0,0 +1,2 @@ +# This should fail +test1 "unterminated string diff --git a/amend/tests/005-lex-quoted-string/input3 b/amend/tests/005-lex-quoted-string/input3 new file mode 100644 index 0000000..02f3f85 --- /dev/null +++ b/amend/tests/005-lex-quoted-string/input3 @@ -0,0 +1,2 @@ +# This should fail +test1 "string with illegal escape \n in the middle" diff --git a/amend/tests/005-lex-quoted-string/run b/amend/tests/005-lex-quoted-string/run new file mode 100644 index 0000000..7b1292a --- /dev/null +++ b/amend/tests/005-lex-quoted-string/run @@ -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 diff --git a/amend/tests/006-lex-words/SKIP b/amend/tests/006-lex-words/SKIP new file mode 100644 index 0000000..e69de29 diff --git a/amend/tests/006-lex-words/expected.txt b/amend/tests/006-lex-words/expected.txt new file mode 100644 index 0000000..a78a0b1 --- /dev/null +++ b/amend/tests/006-lex-words/expected.txt @@ -0,0 +1,6 @@ + IDENTIFIER WORD WORD WORD WORD WORD WORD WORD WORD EOL + IDENTIFIER WORD<12> WORD WORD WORD<)> WORD WORD WORD<\\> WORD WORD EOL + IDENTIFIER WORD WORD WORD WORD WORD WORD EOL + IDENTIFIER WORD WORD WORD EOL + IDENTIFIER WORD<"""shouldn't> WORD WORD WORD WORD EOL + EOF diff --git a/amend/tests/006-lex-words/info.txt b/amend/tests/006-lex-words/info.txt new file mode 100644 index 0000000..dd37016 --- /dev/null +++ b/amend/tests/006-lex-words/info.txt @@ -0,0 +1 @@ +Test to make sure that argument words are tokenized properly. diff --git a/amend/tests/006-lex-words/input b/amend/tests/006-lex-words/input new file mode 100644 index 0000000..a4de638 --- /dev/null +++ b/amend/tests/006-lex-words/input @@ -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 diff --git a/amend/tests/006-lex-words/input2 b/amend/tests/006-lex-words/input2 new file mode 100644 index 0000000..09e6689 --- /dev/null +++ b/amend/tests/006-lex-words/input2 @@ -0,0 +1,2 @@ +# This should fail +test1 "unterminated string diff --git a/amend/tests/006-lex-words/input3 b/amend/tests/006-lex-words/input3 new file mode 100644 index 0000000..02f3f85 --- /dev/null +++ b/amend/tests/006-lex-words/input3 @@ -0,0 +1,2 @@ +# This should fail +test1 "string with illegal escape \n in the middle" diff --git a/amend/tests/006-lex-words/run b/amend/tests/006-lex-words/run new file mode 100644 index 0000000..35c4a4f --- /dev/null +++ b/amend/tests/006-lex-words/run @@ -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 diff --git a/amend/tests/007-lex-real-script/expected.txt b/amend/tests/007-lex-real-script/expected.txt new file mode 100644 index 0000000..012f62c --- /dev/null +++ b/amend/tests/007-lex-real-script/expected.txt @@ -0,0 +1,11 @@ + IDENTIFIER IDENTIFIER ( STRING ) == STRING<112345oldhashvalue1234123> EOL + IDENTIFIER WORD WORD EOL + IDENTIFIER WORD WORD EOL + IDENTIFIER IDENTIFIER ( STRING ) == STRING<667890newhashvalue6678909> EOL + IDENTIFIER WORD WORD EOL + IDENTIFIER EOL + IDENTIFIER IDENTIFIER ( STRING , STRING ) == STRING<112345oldhashvalue1234123> EOL + IDENTIFIER STRING == STRING EOL + IDENTIFIER IDENTIFIER ( STRING , IDENTIFIER ( STRING ) ) == STRING EOL + IDENTIFIER IDENTIFIER ( STRING ) == STRING<667890newhashvalue6678909> || IDENTIFIER ( STRING ) == STRING<667890newhashvalue6678909> EOL + EOF diff --git a/amend/tests/007-lex-real-script/info.txt b/amend/tests/007-lex-real-script/info.txt new file mode 100644 index 0000000..5e321f5 --- /dev/null +++ b/amend/tests/007-lex-real-script/info.txt @@ -0,0 +1 @@ +An input script similar to one that will actually be used in practice. diff --git a/amend/tests/007-lex-real-script/input b/amend/tests/007-lex-real-script/input new file mode 100644 index 0000000..f3f1fd9 --- /dev/null +++ b/amend/tests/007-lex-real-script/input @@ -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" diff --git a/amend/tests/007-lex-real-script/run b/amend/tests/007-lex-real-script/run new file mode 100644 index 0000000..35c4a4f --- /dev/null +++ b/amend/tests/007-lex-real-script/run @@ -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 diff --git a/amend/tests/008-parse-real-script/expected.txt b/amend/tests/008-parse-real-script/expected.txt new file mode 100644 index 0000000..dabf6d4 --- /dev/null +++ b/amend/tests/008-parse-real-script/expected.txt @@ -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. diff --git a/amend/tests/008-parse-real-script/info.txt b/amend/tests/008-parse-real-script/info.txt new file mode 100644 index 0000000..5e321f5 --- /dev/null +++ b/amend/tests/008-parse-real-script/info.txt @@ -0,0 +1 @@ +An input script similar to one that will actually be used in practice. diff --git a/amend/tests/008-parse-real-script/input b/amend/tests/008-parse-real-script/input new file mode 100644 index 0000000..b073306 --- /dev/null +++ b/amend/tests/008-parse-real-script/input @@ -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" diff --git a/amend/tests/008-parse-real-script/run b/amend/tests/008-parse-real-script/run new file mode 100644 index 0000000..9544e1b --- /dev/null +++ b/amend/tests/008-parse-real-script/run @@ -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 diff --git a/amend/tests/XXX-long-token/SKIP b/amend/tests/XXX-long-token/SKIP new file mode 100644 index 0000000..e69de29 diff --git a/amend/tests/XXX-stack-overflow/SKIP b/amend/tests/XXX-stack-overflow/SKIP new file mode 100644 index 0000000..e69de29 diff --git a/amend/tests/one-test b/amend/tests/one-test new file mode 100755 index 0000000..9cebd3f --- /dev/null +++ b/amend/tests/one-test @@ -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 diff --git a/amend/tests/run-all-tests b/amend/tests/run-all-tests new file mode 100755 index 0000000..c696bbd --- /dev/null +++ b/amend/tests/run-all-tests @@ -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 diff --git a/install.c b/install.c index 7710cec..d0d9038 100644 --- a/install.c +++ b/install.c @@ -33,6 +33,7 @@ #include "roots.h" #include "verifier.h" #include "firmware.h" +#include "legacy.h" #define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" #define PUBLIC_KEYS_FILE "/res/keys" @@ -103,7 +104,7 @@ try_update_binary(const char *path, ZipArchive *zip) { const ZipEntry* binary_entry = mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); if (binary_entry == NULL) { - return INSTALL_CORRUPT; + return INSTALL_UPDATE_BINARY_MISSING; } char* binary = "/tmp/update_binary"; @@ -240,6 +241,12 @@ handle_update_package(const char *path, ZipArchive *zip) ui_print("Installing update...\n"); int result = try_update_binary(path, zip); + if (result == INSTALL_UPDATE_BINARY_MISSING) + { + const ZipEntry *script_entry; + script_entry = find_update_script(zip); + result = handle_update_script(zip, script_entry); + } register_package_root(NULL, NULL); // Unregister package root return result; } diff --git a/install.h b/install.h index a7ebc09..42c1444 100644 --- a/install.h +++ b/install.h @@ -19,7 +19,7 @@ #include "common.h" -enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT }; +enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_UPDATE_BINARY_MISSING }; int install_package(const char *root_path); #endif // RECOVERY_INSTALL_H_ diff --git a/legacy.c b/legacy.c new file mode 100644 index 0000000..b50ba3e --- /dev/null +++ b/legacy.c @@ -0,0 +1,98 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#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" +#include "firmware.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" + +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; +} + +#define ASSUMED_UPDATE_SCRIPT_NAME "META-INF/com/google/android/update-script" + +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); +} diff --git a/legacy.h b/legacy.h new file mode 100644 index 0000000..a3ca77c --- /dev/null +++ b/legacy.h @@ -0,0 +1,5 @@ +int +handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry); + +const ZipEntry * +find_update_script(ZipArchive *zip); \ No newline at end of file From 6060e5c6df717a8070ceeb9c72c3c5dafa29cdd7 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Thu, 11 Feb 2010 22:27:06 -0800 Subject: [PATCH 004/233] update.zip somewhat working now... --- Android.mk | 3 + amend/register.c | 16 + commands.c | 1154 +++++++++++++++++++++++++++++++++++++++++ commands.h | 28 + default_recovery_ui.c | 2 + extendedcommands.c | 39 ++ extendedcommands.h | 5 + install.c | 45 +- legacy.c | 35 ++ recovery.c | 14 + recovery_ui.h | 2 + 11 files changed, 1323 insertions(+), 20 deletions(-) create mode 100644 commands.c create mode 100644 commands.h create mode 100644 extendedcommands.c create mode 100644 extendedcommands.h diff --git a/Android.mk b/Android.mk index 6a64d86..66c30cc 100644 --- a/Android.mk +++ b/Android.mk @@ -5,9 +5,12 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) commands_recovery_local_path := $(LOCAL_PATH) +# LOCAL_CPP_EXTENSION := .c LOCAL_SRC_FILES := \ + extendedcommands.c \ legacy.c \ + commands.c \ recovery.c \ bootloader.c \ firmware.c \ diff --git a/amend/register.c b/amend/register.c index 167dd32..e45a973 100644 --- a/amend/register.c +++ b/amend/register.c @@ -125,6 +125,19 @@ cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[], return -1; } +/* delete + */ +static int +cmd_delete(const char *name, void *cookie, int argc, const char *argv[], + PermissionRequestList *permissions) +{ + UNUSED(name); + UNUSED(cookie); + CHECK_WORDS(); +//xxx + return -1; +} + /* mark dirty|clean */ static int @@ -165,6 +178,9 @@ registerUpdateCommands() ret = registerCommand("copy_dir", CMD_ARGS_WORDS, cmd_copy_dir, NULL); if (ret < 0) return ret; + ret = registerCommand("delete", CMD_ARGS_WORDS, cmd_delete, NULL); + if (ret < 0) return ret; + ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, NULL); if (ret < 0) return ret; diff --git a/commands.c b/commands.c new file mode 100644 index 0000000..e6fb9be --- /dev/null +++ b/commands.c @@ -0,0 +1,1154 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "amend/commands.h" +#include "commands.h" +#include "common.h" +#include "cutils/misc.h" +#include "cutils/properties.h" +#include "firmware.h" +#include "minzip/DirUtil.h" +#include "minzip/Zip.h" +#include "roots.h" + +#include "extendedcommands.h" + +static int gDidShowProgress = 0; + +#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 + */ +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 (!script_assert_enabled) { + return 0; + } + + /* 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 + */ +static int +cmd_format(const char *name, void *cookie, int argc, const char *argv[], + PermissionRequestList *permissions) +{ + UNUSED(name); + UNUSED(cookie); + CHECK_WORDS(); + + if (argc != 1) { + LOGE("Command %s requires exactly one argument\n", name); + return 1; + } + const char *root = argv[0]; + ui_print("Formatting %s...\n", root); + + int ret = format_root_device(root); + if (ret != 0) { + LOGE("Can't format %s\n", root); + return 1; + } + + return 0; +} + +/* delete [ ...] + * delete_recursive [ ...] + * + * Like "rm -f", will try to delete every named file/dir, even if + * earlier ones fail. Recursive deletes that fail halfway through + * give up early. + */ +static int +cmd_delete(const char *name, void *cookie, int argc, const char *argv[], + PermissionRequestList *permissions) +{ + UNUSED(cookie); + CHECK_WORDS(); + int nerr = 0; + bool recurse; + + if (argc < 1) { + LOGE("Command %s requires at least one argument\n", name); + return 1; + } + + recurse = (strcmp(name, "delete_recursive") == 0); + ui_print("Deleting files...\n"); +//xxx permissions + + int i; + for (i = 0; i < argc; i++) { + const char *root_path = argv[i]; + char pathbuf[PATH_MAX]; + const char *path; + + /* This guarantees that all paths use "SYSTEM:"-style roots; + * plain paths won't make it through translate_root_path(). + */ + path = translate_root_path(root_path, pathbuf, sizeof(pathbuf)); + if (path != NULL) { + int ret = ensure_root_path_mounted(root_path); + if (ret < 0) { + LOGW("Can't mount volume to delete \"%s\"\n", root_path); + nerr++; + continue; + } + if (recurse) { + ret = dirUnlinkHierarchy(path); + } else { + ret = unlink(path); + } + if (ret != 0 && errno != ENOENT) { + LOGW("Can't delete %s\n(%s)\n", path, strerror(errno)); + nerr++; + } + } else { + nerr++; + } + } +//TODO: add a way to fail if a delete didn't work + + return 0; +} + +typedef struct { + int num_done; + int num_total; +} ExtractContext; + +static void extract_count_cb(const char *fn, void *cookie) +{ + ++((ExtractContext*) cookie)->num_total; +} + +static void extract_cb(const char *fn, void *cookie) +{ + // minzip writes the filename to the log, so we don't need to + ExtractContext *ctx = (ExtractContext*) cookie; + ui_set_progress((float) ++ctx->num_done / ctx->num_total); +} + +/* copy_dir [] + * + * The contents of will become the contents of . + * The original contents of are preserved unless something + * in overwrote them. + * + * e.g., for "copy_dir PKG:system SYSTEM:", the file "PKG:system/a" + * would be copied to "SYSTEM:a". + * + * The specified timestamp (in decimal seconds since 1970) will be used, + * or a fixed default timestamp will be supplied otherwise. + */ +static int +cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[], + PermissionRequestList *permissions) +{ + UNUSED(name); + UNUSED(cookie); + CHECK_WORDS(); +//xxx permissions + + // To create a consistent system image, never use the clock for timestamps. + struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default + if (argc == 3) { + char *end; + time_t value = strtoul(argv[2], &end, 0); + if (value == 0 || end[0] != '\0') { + LOGE("Command %s: invalid timestamp \"%s\"\n", name, argv[2]); + return 1; + } else if (value < timestamp.modtime) { + LOGE("Command %s: timestamp \"%s\" too early\n", name, argv[2]); + return 1; + } + timestamp.modtime = timestamp.actime = value; + } else if (argc != 2) { + LOGE("Command %s requires exactly two arguments\n", name); + return 1; + } + + // Use 40% of the progress bar (80% post-verification) by default + ui_print("Copying files...\n"); + if (!gDidShowProgress) ui_show_progress(DEFAULT_FILES_PROGRESS_FRACTION, 0); + + /* Mount the destination volume if it isn't already. + */ + const char *dst_root_path = argv[1]; + int ret = ensure_root_path_mounted(dst_root_path); + if (ret < 0) { + LOGE("Can't mount %s\n", dst_root_path); + return 1; + } + + /* Get the real target path. + */ + char dstpathbuf[PATH_MAX]; + const char *dst_path; + dst_path = translate_root_path(dst_root_path, + dstpathbuf, sizeof(dstpathbuf)); + if (dst_path == NULL) { + LOGE("Command %s: bad destination path \"%s\"\n", name, dst_root_path); + return 1; + } + + /* Try to copy the directory. The source may be inside a package. + */ + const char *src_root_path = argv[0]; + char srcpathbuf[PATH_MAX]; + const char *src_path; + if (is_package_root_path(src_root_path)) { + const ZipArchive *package; + src_path = translate_package_root_path(src_root_path, + srcpathbuf, sizeof(srcpathbuf), &package); + if (src_path == NULL) { + LOGE("Command %s: bad source path \"%s\"\n", name, src_root_path); + return 1; + } + + /* Extract the files. Set MZ_EXTRACT_FILES_ONLY, because only files + * are validated by the signature. Do a dry run first to count how + * many there are (and find some errors early). + */ + ExtractContext ctx; + ctx.num_done = 0; + ctx.num_total = 0; + + if (!mzExtractRecursive(package, src_path, dst_path, + MZ_EXTRACT_FILES_ONLY | MZ_EXTRACT_DRY_RUN, + ×tamp, extract_count_cb, (void *) &ctx) || + !mzExtractRecursive(package, src_path, dst_path, + MZ_EXTRACT_FILES_ONLY, + ×tamp, extract_cb, (void *) &ctx)) { + LOGW("Command %s: couldn't extract \"%s\" to \"%s\"\n", + name, src_root_path, dst_root_path); + return 1; + } + } else { + LOGE("Command %s: non-package source path \"%s\" not yet supported\n", + name, src_root_path); +//xxx mount the src volume +//xxx + return 255; + } + + return 0; +} + +/* run_program [ ...] + * + * Run an external program included in the update package. + */ +static int +cmd_run_program(const char *name, void *cookie, int argc, const char *argv[], + PermissionRequestList *permissions) +{ + UNUSED(cookie); + CHECK_WORDS(); + + if (argc < 1) { + LOGE("Command %s requires at least one argument\n", name); + return 1; + } + + // Copy the program file to temporary storage. + if (!is_package_root_path(argv[0])) { + LOGE("Command %s: non-package program file \"%s\" not supported\n", + name, argv[0]); + return 1; + } + + char path[PATH_MAX]; + const ZipArchive *package; + if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) { + LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]); + return 1; + } + + const ZipEntry *entry = mzFindZipEntry(package, path); + if (entry == NULL) { + LOGE("Can't find %s\n", path); + return 1; + } + + static const char *binary = "/tmp/run_program_binary"; + unlink(binary); // just to be sure + int fd = creat(binary, 0755); + if (fd < 0) { + LOGE("Can't make %s\n", binary); + return 1; + } + bool ok = mzExtractZipEntryToFile(package, entry, fd); + close(fd); + + if (!ok) { + LOGE("Can't copy %s\n", path); + return 1; + } + + // Create a copy of argv to NULL-terminate it, as execv requires + char **args = (char **) malloc(sizeof(char*) * (argc + 1)); + memcpy(args, argv, sizeof(char*) * argc); + args[argc] = NULL; + + pid_t pid = fork(); + if (pid == 0) { + execv(binary, args); + fprintf(stderr, "E:Can't run %s\n(%s)\n", binary, strerror(errno)); + _exit(-1); + } + + int status; + waitpid(pid, &status, 0); + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + return 0; + } else { + LOGE("Error in %s\n(Status %d)\n", path, status); + return 1; + } +} + +/* set_perm [... ] + * set_perm_recursive [... ] + * + * Like "chmod", "chown" and "chgrp" all in one, set ownership and permissions + * of single files or entire directory trees. Any error causes failure. + * User, group, and modes must all be integer values (hex or octal OK). + */ +static int +cmd_set_perm(const char *name, void *cookie, int argc, const char *argv[], + PermissionRequestList *permissions) +{ + UNUSED(cookie); + CHECK_WORDS(); + bool recurse = !strcmp(name, "set_perm_recursive"); + + int min_args = 4 + (recurse ? 1 : 0); + if (argc < min_args) { + LOGE("Command %s requires at least %d args\n", name, min_args); + return 1; + } + + // All the arguments except the path(s) are numeric. + int i, n[min_args - 1]; + for (i = 0; i < min_args - 1; ++i) { + char *end; + n[i] = strtoul(argv[i], &end, 0); + if (end[0] != '\0' || argv[i][0] == '\0') { + LOGE("Command %s: invalid argument \"%s\"\n", name, argv[i]); + return 1; + } + } + + for (i = min_args - 1; i < min_args; ++i) { + char path[PATH_MAX]; + if (translate_root_path(argv[i], path, sizeof(path)) == NULL) { + LOGE("Command %s: bad path \"%s\"\n", name, argv[i]); + return 1; + } + + if (ensure_root_path_mounted(argv[i])) { + LOGE("Can't mount %s\n", argv[i]); + return 1; + } + + if (recurse + ? dirSetHierarchyPermissions(path, n[0], n[1], n[2], n[3]) + : (chown(path, n[0], n[1]) || chmod(path, n[2]))) { + LOGE("Can't chown/mod %s\n(%s)\n", path, strerror(errno)); + return 1; + } + } + + return 0; +} + +/* show_progress + * + * Use of the on-screen progress meter for the next operation, + * automatically advancing the meter over seconds (or more rapidly + * if the actual rate of progress can be determined). + */ +static int +cmd_show_progress(const char *name, void *cookie, int argc, const char *argv[], + PermissionRequestList *permissions) +{ + UNUSED(cookie); + CHECK_WORDS(); + + if (argc != 2) { + LOGE("Command %s requires exactly two arguments\n", name); + return 1; + } + + char *end; + double fraction = strtod(argv[0], &end); + if (end[0] != '\0' || argv[0][0] == '\0' || fraction < 0 || fraction > 1) { + LOGE("Command %s: invalid fraction \"%s\"\n", name, argv[0]); + return 1; + } + + int duration = strtoul(argv[1], &end, 0); + if (end[0] != '\0' || argv[0][0] == '\0') { + LOGE("Command %s: invalid duration \"%s\"\n", name, argv[1]); + return 1; + } + + // Half of the progress bar is taken by verification, + // so everything that happens during installation is scaled. + ui_show_progress(fraction * (1 - VERIFICATION_PROGRESS_FRACTION), duration); + gDidShowProgress = 1; + return 0; +} + +/* symlink + * + * Create a symlink, like "ln -s". The link path must not exist already. + * Note that is in root:path format, but is + * for the target filesystem (and may be relative). + */ +static int +cmd_symlink(const char *name, void *cookie, int argc, const char *argv[], + PermissionRequestList *permissions) +{ + UNUSED(cookie); + CHECK_WORDS(); + + if (argc != 2) { + LOGE("Command %s requires exactly two arguments\n", name); + return 1; + } + + char path[PATH_MAX]; + if (translate_root_path(argv[1], path, sizeof(path)) == NULL) { + LOGE("Command %s: bad path \"%s\"\n", name, argv[1]); + return 1; + } + + if (ensure_root_path_mounted(argv[1])) { + LOGE("Can't mount %s\n", argv[1]); + return 1; + } + + if (symlink(argv[0], path)) { + LOGE("Can't symlink %s\n", path); + return 1; + } + + return 0; +} + +struct FirmwareContext { + size_t total_bytes, done_bytes; + char *data; +}; + +static bool firmware_fn(const unsigned char *data, int data_len, void *cookie) +{ + struct FirmwareContext *context = (struct FirmwareContext*) cookie; + if (context->done_bytes + data_len > context->total_bytes) { + LOGE("Data overrun in firmware\n"); + return false; // Should not happen, but let's be safe. + } + + memcpy(context->data + context->done_bytes, data, data_len); + context->done_bytes += data_len; + ui_set_progress(context->done_bytes * 1.0 / context->total_bytes); + return true; +} + +/* write_radio_image + * write_hboot_image + * Doesn't actually take effect until the rest of installation finishes. + */ +static int +cmd_write_firmware_image(const char *name, void *cookie, + int argc, const char *argv[], PermissionRequestList *permissions) +{ + UNUSED(cookie); + CHECK_WORDS(); + + if (argc != 1) { + LOGE("Command %s requires exactly one argument\n", name); + return 1; + } + + const char *type; + if (!strcmp(name, "write_radio_image")) { + type = "radio"; + } else if (!strcmp(name, "write_hboot_image")) { + type = "hboot"; + } else { + LOGE("Unknown firmware update command %s\n", name); + return 1; + } + + if (!is_package_root_path(argv[0])) { + LOGE("Command %s: non-package image file \"%s\" not supported\n", + name, argv[0]); + return 1; + } + + ui_print("Extracting %s image...\n", type); + char path[PATH_MAX]; + const ZipArchive *package; + if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) { + LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]); + return 1; + } + + const ZipEntry *entry = mzFindZipEntry(package, path); + if (entry == NULL) { + LOGE("Can't find %s\n", path); + return 1; + } + + // Load the update image into RAM. + struct FirmwareContext context; + context.total_bytes = mzGetZipEntryUncompLen(entry); + context.done_bytes = 0; + context.data = malloc(context.total_bytes); + if (context.data == NULL) { + LOGE("Can't allocate %d bytes for %s\n", context.total_bytes, argv[0]); + return 1; + } + + if (!mzProcessZipEntryContents(package, entry, firmware_fn, &context) || + context.done_bytes != context.total_bytes) { + LOGE("Can't read %s\n", argv[0]); + free(context.data); + return 1; + } + + if (remember_firmware_update(type, context.data, context.total_bytes)) { + LOGE("Can't store %s image\n", type); + free(context.data); + return 1; + } + + return 0; +} + +static bool write_raw_image_process_fn( + const unsigned char *data, + int data_len, void *ctx) +{ + int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len); + if (r == data_len) return true; + LOGE("%s\n", strerror(errno)); + return false; +} + +/* write_raw_image + */ +static int +cmd_write_raw_image(const char *name, void *cookie, + int argc, const char *argv[], PermissionRequestList *permissions) +{ + UNUSED(cookie); + CHECK_WORDS(); +//xxx permissions + + if (argc != 2) { + LOGE("Command %s requires exactly two arguments\n", name); + return 1; + } + + // Use 10% of the progress bar (20% post-verification) by default + const char *src_root_path = argv[0]; + const char *dst_root_path = argv[1]; + ui_print("Writing %s...\n", dst_root_path); + if (!gDidShowProgress) ui_show_progress(DEFAULT_IMAGE_PROGRESS_FRACTION, 0); + + /* Find the source image, which is probably in a package. + */ + if (!is_package_root_path(src_root_path)) { + LOGE("Command %s: non-package source path \"%s\" not yet supported\n", + name, src_root_path); + return 255; + } + + /* Get the package. + */ + char srcpathbuf[PATH_MAX]; + const char *src_path; + const ZipArchive *package; + src_path = translate_package_root_path(src_root_path, + srcpathbuf, sizeof(srcpathbuf), &package); + if (src_path == NULL) { + LOGE("Command %s: bad source path \"%s\"\n", name, src_root_path); + return 1; + } + + /* Get the entry. + */ + const ZipEntry *entry = mzFindZipEntry(package, src_path); + if (entry == NULL) { + LOGE("Missing file %s\n", src_path); + return 1; + } + + /* Unmount the destination root if it isn't already. + */ + int ret = ensure_root_path_unmounted(dst_root_path); + if (ret < 0) { + LOGE("Can't unmount %s\n", dst_root_path); + return 1; + } + + /* Open the partition for writing. + */ + const MtdPartition *partition = get_root_mtd_partition(dst_root_path); + if (partition == NULL) { + LOGE("Can't find %s\n", dst_root_path); + return 1; + } + MtdWriteContext *context = mtd_write_partition(partition); + if (context == NULL) { + LOGE("Can't open %s\n", dst_root_path); + return 1; + } + + /* Extract and write the image. + */ + bool ok = mzProcessZipEntryContents(package, entry, + write_raw_image_process_fn, context); + if (!ok) { + LOGE("Error writing %s\n", dst_root_path); + mtd_write_close(context); + return 1; + } + + if (mtd_erase_blocks(context, -1) == (off_t) -1) { + LOGE("Error finishing %s\n", dst_root_path); + mtd_write_close(context); + return -1; + } + + if (mtd_write_close(context)) { + LOGE("Error closing %s\n", dst_root_path); + return -1; + } + return 0; +} + +/* mark 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; +} + + +/* + * Function definitions + */ + +/* compatible_with() + * + * Returns "true" if this version of the script parser and command + * set supports the named version. + */ +static int +fn_compatible_with(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; + } + + if (!strcmp(argv[0], "0.1") || !strcmp(argv[0], "0.2")) { + *result = strdup("true"); + } else { + *result = strdup(""); + } + if (resultLen != NULL) { + *resultLen = strlen(*result); + } + return 0; +} + +/* 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() + * + * 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() + */ +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(, [, ...]) + * If matches (strcmp) any of ..., returns , + * 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(, [, ...]) + * 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; +} + +/* getprop() + * Returns the named Android system property value, or "" if not set. + */ +static int +fn_getprop(const char *name, void *cookie, int argc, const char *argv[], + char **result, size_t *resultLen, + PermissionRequestList *permissions) +{ + UNUSED(cookie); + CHECK_FN(); + NO_PERMS(permissions); + + if (argc != 1) { + LOGE("Command %s requires exactly one argument\n", name); + return 1; + } + + char value[PROPERTY_VALUE_MAX]; + property_get(argv[0], value, ""); + + *result = strdup(value); + if (resultLen != NULL) { + *resultLen = strlen(*result); + } + + return 0; +} + +/* file_contains(, ) + * Returns "true" if the file exists and contains the specified substring. + */ +static int +fn_file_contains(const char *name, void *cookie, int argc, const char *argv[], + char **result, size_t *resultLen, + PermissionRequestList *permissions) +{ + UNUSED(cookie); + CHECK_FN(); + NO_PERMS(permissions); + + if (argc != 2) { + LOGE("Command %s requires exactly two arguments\n", name); + return 1; + } + + char pathbuf[PATH_MAX]; + const char *root_path = argv[0]; + const char *path = translate_root_path(root_path, pathbuf, sizeof(pathbuf)); + if (path == NULL) { + LOGE("Command %s: bad path \"%s\"\n", name, root_path); + return 1; + } + + if (ensure_root_path_mounted(root_path)) { + LOGE("Can't mount %s\n", root_path); + return 1; + } + + const char *needle = argv[1]; + char *haystack = (char*) load_file(path, NULL); + if (haystack == NULL) { + LOGI("%s: Can't read \"%s\" (%s)\n", name, path, strerror(errno)); + *result = ""; /* File not found is not an error. */ + } else if (strstr(haystack, needle) == NULL) { + LOGI("%s: Can't find \"%s\" in \"%s\"\n", name, needle, path); + *result = strdup(""); + free(haystack); + } else { + *result = strdup("true"); + free(haystack); + } + + if (resultLen != NULL) { + *resultLen = strlen(*result); + } + return 0; +} + +int +register_update_commands(RecoveryCommandContext *ctx) +{ + int ret; + + ret = commandInit(); + if (ret < 0) return ret; + + /* + * Commands + */ + + ret = registerCommand("assert", CMD_ARGS_BOOLEAN, cmd_assert, (void *)ctx); + if (ret < 0) return ret; + + ret = registerCommand("delete", CMD_ARGS_WORDS, cmd_delete, (void *)ctx); + if (ret < 0) return ret; + + ret = registerCommand("delete_recursive", CMD_ARGS_WORDS, cmd_delete, + (void *)ctx); + if (ret < 0) return ret; + + ret = registerCommand("copy_dir", CMD_ARGS_WORDS, + cmd_copy_dir, (void *)ctx); + if (ret < 0) return ret; + + ret = registerCommand("run_program", CMD_ARGS_WORDS, + cmd_run_program, (void *)ctx); + if (ret < 0) return ret; + + ret = registerCommand("set_perm", CMD_ARGS_WORDS, + cmd_set_perm, (void *)ctx); + if (ret < 0) return ret; + + ret = registerCommand("set_perm_recursive", CMD_ARGS_WORDS, + cmd_set_perm, (void *)ctx); + if (ret < 0) return ret; + + ret = registerCommand("show_progress", CMD_ARGS_WORDS, + cmd_show_progress, (void *)ctx); + if (ret < 0) return ret; + + ret = registerCommand("symlink", CMD_ARGS_WORDS, cmd_symlink, (void *)ctx); + if (ret < 0) return ret; + + ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, (void *)ctx); + if (ret < 0) return ret; + + ret = registerCommand("write_radio_image", CMD_ARGS_WORDS, + cmd_write_firmware_image, (void *)ctx); + if (ret < 0) return ret; + + ret = registerCommand("write_hboot_image", CMD_ARGS_WORDS, + cmd_write_firmware_image, (void *)ctx); + if (ret < 0) return ret; + + ret = registerCommand("write_raw_image", CMD_ARGS_WORDS, + cmd_write_raw_image, (void *)ctx); + if (ret < 0) return ret; + + ret = registerCommand("mark", CMD_ARGS_WORDS, cmd_mark, (void *)ctx); + if (ret < 0) return ret; + + ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, (void *)ctx); + if (ret < 0) return ret; + + /* + * Functions + */ + + ret = registerFunction("compatible_with", fn_compatible_with, (void *)ctx); + if (ret < 0) return ret; + + ret = registerFunction("update_forced", fn_update_forced, (void *)ctx); + if (ret < 0) return ret; + + ret = registerFunction("get_mark", fn_get_mark, (void *)ctx); + if (ret < 0) return ret; + + ret = registerFunction("hash_dir", fn_hash_dir, (void *)ctx); + if (ret < 0) return ret; + + ret = registerFunction("matches", fn_matches, (void *)ctx); + if (ret < 0) return ret; + + ret = registerFunction("concat", fn_concat, (void *)ctx); + if (ret < 0) return ret; + + ret = registerFunction("getprop", fn_getprop, (void *)ctx); + if (ret < 0) return ret; + + ret = registerFunction("file_contains", fn_file_contains, (void *)ctx); + if (ret < 0) return ret; + + return 0; +} diff --git a/commands.h b/commands.h new file mode 100644 index 0000000..e9acea2 --- /dev/null +++ b/commands.h @@ -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_ diff --git a/default_recovery_ui.c b/default_recovery_ui.c index cb76b9a..025ee98 100644 --- a/default_recovery_ui.c +++ b/default_recovery_ui.c @@ -27,6 +27,8 @@ char* MENU_ITEMS[] = { "reboot system now", "apply sdcard:update.zip", "wipe data/factory reset", "wipe cache partition", + "toggle signature verification", + "toggle script asserts", NULL }; int device_toggle_display(volatile char* key_pressed, int key_code) { diff --git a/extendedcommands.c b/extendedcommands.c new file mode 100644 index 0000000..b2aacc9 --- /dev/null +++ b/extendedcommands.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bootloader.h" +#include "common.h" +#include "cutils/properties.h" +#include "firmware.h" +#include "install.h" +#include "minui/minui.h" +#include "minzip/DirUtil.h" +#include "roots.h" +#include "recovery_ui.h" + +int signature_check_enabled = 1; +int script_assert_enabled = 1; + +void +toggle_signature_check() +{ + signature_check_enabled = !signature_check_enabled; + ui_print("Signature Check: %s\n", signature_check_enabled ? "Enabled" : "Disabled"); +} + +void toggle_script_asserts() +{ + script_assert_enabled = !script_assert_enabled; + ui_print("Script Asserts: %s\n", signature_check_enabled ? "Enabled" : "Disabled"); +} \ No newline at end of file diff --git a/extendedcommands.h b/extendedcommands.h new file mode 100644 index 0000000..5b82960 --- /dev/null +++ b/extendedcommands.h @@ -0,0 +1,5 @@ +extern int signature_check_enabled; +extern int script_assert_enabled; + +void +toggle_signature_check(); \ No newline at end of file diff --git a/install.c b/install.c index d0d9038..5a829d1 100644 --- a/install.c +++ b/install.c @@ -35,6 +35,8 @@ #include "firmware.h" #include "legacy.h" +#include "extendedcommands.h" + #define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" #define PUBLIC_KEYS_FILE "/res/keys" @@ -347,27 +349,30 @@ install_package(const char *root_path) ui_print("Opening update package...\n"); LOGI("Update file path: %s\n", path); - int numKeys; - RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); - if (loadedKeys == NULL) { - LOGE("Failed to load keys\n"); - return INSTALL_CORRUPT; - } - LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE); - - // Give verification half the progress bar... - ui_print("Verifying update package...\n"); - ui_show_progress( - VERIFICATION_PROGRESS_FRACTION, - VERIFICATION_PROGRESS_TIME); - int err; - err = verify_file(path, loadedKeys, numKeys); - free(loadedKeys); - LOGI("verify_file returned %d\n", err); - if (err != VERIFY_SUCCESS) { - LOGE("signature verification failed\n"); - return INSTALL_CORRUPT; + + if (signature_check_enabled) { + int numKeys; + RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); + if (loadedKeys == NULL) { + LOGE("Failed to load keys\n"); + return INSTALL_CORRUPT; + } + LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE); + + // Give verification half the progress bar... + ui_print("Verifying update package...\n"); + ui_show_progress( + VERIFICATION_PROGRESS_FRACTION, + VERIFICATION_PROGRESS_TIME); + + err = verify_file(path, loadedKeys, numKeys); + free(loadedKeys); + LOGI("verify_file returned %d\n", err); + if (err != VERIFY_SUCCESS) { + LOGE("signature verification failed\n"); + return INSTALL_CORRUPT; + } } /* Try to open the package. diff --git a/legacy.c b/legacy.c index b50ba3e..dab06ad 100644 --- a/legacy.c +++ b/legacy.c @@ -46,9 +46,44 @@ #include "roots.h" #include "verifier.h" +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; +} + int handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) { + // This is bizarre. The build fails with "undefined reference" + // unless the following two functions are referenced from somewhere to + // force them to be linked. This seems to be a problem with yacc/lex. + if (zip == 1) + { + fwrite(NULL, 0, 0, NULL); + fileno(NULL); + } + /* Read the entire script into a buffer. */ int script_len; diff --git a/recovery.c b/recovery.c index 5b3f6e5..b857610 100644 --- a/recovery.c +++ b/recovery.c @@ -38,6 +38,9 @@ #include "roots.h" #include "recovery_ui.h" +#include "extendedcommands.h" +#include "commands.h" + static const struct option OPTIONS[] = { { "send_intent", required_argument, NULL, 's' }, { "update_package", required_argument, NULL, 'u' }, @@ -430,6 +433,12 @@ prompt_and_wait() } } break; + case ITEM_SIG_CHECK: + toggle_signature_check(); + break; + case ITEM_ASSERTS: + toggle_script_asserts(); + break; } } } @@ -482,6 +491,11 @@ main(int argc, char **argv) fprintf(stderr, "\n"); int status = INSTALL_SUCCESS; + + RecoveryCommandContext ctx = { NULL }; + if (register_update_commands(&ctx)) { + LOGE("Can't install update commands\n"); + } if (update_package != NULL) { status = install_package(update_package); diff --git a/recovery_ui.h b/recovery_ui.h index 8818ef3..1aabbdf 100644 --- a/recovery_ui.h +++ b/recovery_ui.h @@ -66,6 +66,8 @@ int device_wipe_data(); #define ITEM_APPLY_SDCARD 1 #define ITEM_WIPE_DATA 2 #define ITEM_WIPE_CACHE 3 +#define ITEM_SIG_CHECK 4 +#define ITEM_ASSERTS 5 // Header text to display above the main menu. extern char* MENU_HEADERS[]; From 1f14c9a1f1dd29dbef6a96e051cd992a7aa593fc Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Thu, 11 Feb 2010 23:08:23 -0800 Subject: [PATCH 005/233] Need to call register_package_root prior to installing the package. The subsequent unregistering made me think that it was being registered at some point, but it was removed in the transition to eclair. The unregistration was dead code. It is being used now though. --- install.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/install.c b/install.c index 5a829d1..3a37b4a 100644 --- a/install.c +++ b/install.c @@ -243,12 +243,16 @@ handle_update_package(const char *path, ZipArchive *zip) ui_print("Installing update...\n"); int result = try_update_binary(path, zip); - if (result == INSTALL_UPDATE_BINARY_MISSING) - { - const ZipEntry *script_entry; - script_entry = find_update_script(zip); - result = handle_update_script(zip, script_entry); - } + if (result == INSTALL_UPDATE_BINARY_MISSING) + { + if (register_package_root(zip, path) < 0) { + LOGE("Can't register package root\n"); + return INSTALL_ERROR; + } + const ZipEntry *script_entry; + script_entry = find_update_script(zip); + result = handle_update_script(zip, script_entry); + } register_package_root(NULL, NULL); // Unregister package root return result; } From e923487ff6ae9978959abacf421e018bcb013c10 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Fri, 12 Feb 2010 00:43:24 -0800 Subject: [PATCH 006/233] working recovery image! --- default_recovery_ui.c | 4 ++ extendedcommands.c | 101 +++++++++++++++++++++++++++++++++++++++++- extendedcommands.h | 8 +++- install.c | 26 ++++++----- install.h | 2 +- legacy.c | 2 +- recovery.c | 9 +++- recovery_ui.h | 12 ++++- 8 files changed, 145 insertions(+), 19 deletions(-) diff --git a/default_recovery_ui.c b/default_recovery_ui.c index 025ee98..3514477 100644 --- a/default_recovery_ui.c +++ b/default_recovery_ui.c @@ -27,6 +27,7 @@ char* MENU_ITEMS[] = { "reboot system now", "apply sdcard:update.zip", "wipe data/factory reset", "wipe cache partition", + "install zip from sdcard", "toggle signature verification", "toggle script asserts", NULL }; @@ -56,6 +57,9 @@ int device_handle_key(int key_code, int visible) { case KEY_ENTER: case BTN_MOUSE: return SELECT_ITEM; + case KEY_BACKSPACE: + case KEY_END: + return GO_BACK; } } diff --git a/extendedcommands.c b/extendedcommands.c index b2aacc9..4ef23f7 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -12,6 +12,10 @@ #include #include +#include +#include +#include + #include "bootloader.h" #include "common.h" #include "cutils/properties.h" @@ -35,5 +39,100 @@ toggle_signature_check() void toggle_script_asserts() { script_assert_enabled = !script_assert_enabled; - ui_print("Script Asserts: %s\n", signature_check_enabled ? "Enabled" : "Disabled"); + ui_print("Script Asserts: %s\n", script_assert_enabled ? "Enabled" : "Disabled"); +} + +void show_choose_zip_menu() +{ + static char* headers[] = { "Choose a zip or press POWER to return", + "", + NULL }; + + char path[PATH_MAX] = ""; + DIR *dir; + struct dirent *de; + int total = 0; + int i; + char** files; + char** list; + + if (ensure_root_path_mounted("SDCARD:") != 0) { + LOGE ("Can't mount /sdcard\n"); + return; + } + + dir = opendir("/sdcard"); + if (dir == NULL) { + LOGE("Couldn't open /sdcard"); + return; + } + + const char *extension = ".zip"; + const int extension_length = strlen(extension); + + while ((de=readdir(dir)) != NULL) { + if (de->d_name[0] != '.' && strlen(de->d_name) > extension_length && strcmp(de->d_name + strlen(de->d_name) - extension_length, extension) == 0) { + total++; + } + } + + if (total==0) { + LOGE("No tar archives found\n"); + if(closedir(dir) < 0) { + LOGE("Failed to close directory /sdcard"); + return; + } + } + else { + files = (char**) malloc((total+1)*sizeof(char*)); + files[total]=NULL; + + list = (char**) malloc((total+1)*sizeof(char*)); + list[total]=NULL; + + rewinddir(dir); + + i = 0; + while ((de = readdir(dir)) != NULL) { + if (de->d_name[0] != '.' && strlen(de->d_name) > extension_length && strcmp(de->d_name + strlen(de->d_name) - extension_length, extension) == 0) { + files[i] = (char*) malloc(strlen("/sdcard/")+strlen(de->d_name)+1); + strcpy(files[i], "/sdcard/"); + strcat(files[i], de->d_name); + + list[i] = (char*) malloc(strlen(de->d_name)+1); + strcpy(list[i], de->d_name); + + i++; + } + } + + if (closedir(dir) <0) { + LOGE("Failure closing directory /sdcard\n"); + return; + } + + int chosen_item = get_menu_selection(headers, list, 1); + if (chosen_item >= 0 && chosen_item != GO_BACK) { + char sdcard_package_file[1024]; + strcpy(sdcard_package_file, "SDCARD:"); + strcat(sdcard_package_file, files[chosen_item] + strlen("/sdcard")); + + ui_print("\n-- Install from sdcard...\n"); + set_sdcard_update_bootloader_message(); + int status = install_package(sdcard_package_file); + if (status != INSTALL_SUCCESS) { + ui_set_background(BACKGROUND_ICON_ERROR); + ui_print("Installation aborted.\n"); + } else if (!ui_text_visible()) { + return; // reboot if logs aren't visible + } else { + if (firmware_update_pending()) { + ui_print("\nReboot via menu to complete\n" + "installation.\n"); + } else { + ui_print("\nInstall from sdcard complete.\n"); + } + } + } + } } \ No newline at end of file diff --git a/extendedcommands.h b/extendedcommands.h index 5b82960..a4d4992 100644 --- a/extendedcommands.h +++ b/extendedcommands.h @@ -2,4 +2,10 @@ extern int signature_check_enabled; extern int script_assert_enabled; void -toggle_signature_check(); \ No newline at end of file +toggle_signature_check(); + +void +toggle_script_asserts(); + +void +show_choose_zip_menu(); \ No newline at end of file diff --git a/install.c b/install.c index 3a37b4a..5768960 100644 --- a/install.c +++ b/install.c @@ -106,7 +106,7 @@ try_update_binary(const char *path, ZipArchive *zip) { const ZipEntry* binary_entry = mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); if (binary_entry == NULL) { - return INSTALL_UPDATE_BINARY_MISSING; + return INSTALL_ERROR; } char* binary = "/tmp/update_binary"; @@ -242,17 +242,21 @@ handle_update_package(const char *path, ZipArchive *zip) // Update should take the rest of the progress bar. ui_print("Installing update...\n"); - int result = try_update_binary(path, zip); - if (result == INSTALL_UPDATE_BINARY_MISSING) - { - if (register_package_root(zip, path) < 0) { - LOGE("Can't register package root\n"); - return INSTALL_ERROR; - } - const ZipEntry *script_entry; - script_entry = find_update_script(zip); - result = handle_update_script(zip, script_entry); + // Try installing via the update-script first, because we + // have more control over the asserts it may contain. + // If it does not exist, try the update-binary. + if (register_package_root(zip, path) < 0) { + LOGE("Can't register package root\n"); + return INSTALL_ERROR; } + const ZipEntry *script_entry; + script_entry = find_update_script(zip); + int result = handle_update_script(zip, script_entry); + if (result == INSTALL_UPDATE_SCRIPT_MISSING) + { + result = try_update_binary(path, zip); + } + register_package_root(NULL, NULL); // Unregister package root return result; } diff --git a/install.h b/install.h index 42c1444..3ee814b 100644 --- a/install.h +++ b/install.h @@ -19,7 +19,7 @@ #include "common.h" -enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_UPDATE_BINARY_MISSING }; +enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_UPDATE_SCRIPT_MISSING }; int install_package(const char *root_path); #endif // RECOVERY_INSTALL_H_ diff --git a/legacy.c b/legacy.c index dab06ad..cf903ce 100644 --- a/legacy.c +++ b/legacy.c @@ -90,7 +90,7 @@ handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) 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; + return INSTALL_UPDATE_SCRIPT_MISSING; } /* Parse the script. Note that the script and parse tree are never freed. diff --git a/recovery.c b/recovery.c index b857610..7a12e10 100644 --- a/recovery.c +++ b/recovery.c @@ -212,7 +212,7 @@ get_args(int *argc, char ***argv) { set_bootloader_message(&boot); } -static void +void set_sdcard_update_bootloader_message() { struct bootloader_message boot; @@ -303,7 +303,7 @@ prepend_title(char** headers) { return new_headers; } -static int +int get_menu_selection(char** headers, char** items, int menu_only) { // throw away keys pressed previously, so user doesn't // accidentally trigger menu items. @@ -334,6 +334,8 @@ get_menu_selection(char** headers, char** items, int menu_only) { break; case NO_ACTION: break; + case GO_BACK: + return GO_BACK; } } else if (!menu_only) { chosen_item = action; @@ -439,6 +441,9 @@ prompt_and_wait() case ITEM_ASSERTS: toggle_script_asserts(); break; + case ITEM_INSTALL_ZIP: + show_choose_zip_menu(); + break; } } } diff --git a/recovery_ui.h b/recovery_ui.h index 1aabbdf..4d3d3e2 100644 --- a/recovery_ui.h +++ b/recovery_ui.h @@ -61,13 +61,15 @@ int device_wipe_data(); #define HIGHLIGHT_UP -2 #define HIGHLIGHT_DOWN -3 #define SELECT_ITEM -4 +#define GO_BACK -5 #define ITEM_REBOOT 0 #define ITEM_APPLY_SDCARD 1 #define ITEM_WIPE_DATA 2 #define ITEM_WIPE_CACHE 3 -#define ITEM_SIG_CHECK 4 -#define ITEM_ASSERTS 5 +#define ITEM_INSTALL_ZIP 4 +#define ITEM_SIG_CHECK 5 +#define ITEM_ASSERTS 6 // Header text to display above the main menu. extern char* MENU_HEADERS[]; @@ -75,4 +77,10 @@ extern char* MENU_HEADERS[]; // Text of menu items. extern char* MENU_ITEMS[]; +int +get_menu_selection(char** headers, char** items, int menu_only); + +void +set_sdcard_update_bootloader_message(); + #endif From 3a976a79bfa516644228bcd5ee447cab96deafb6 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Sat, 13 Feb 2010 15:33:49 -0800 Subject: [PATCH 007/233] power button toggles menu --- default_recovery_ui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/default_recovery_ui.c b/default_recovery_ui.c index 3514477..991f376 100644 --- a/default_recovery_ui.c +++ b/default_recovery_ui.c @@ -36,7 +36,7 @@ int device_toggle_display(volatile char* key_pressed, int key_code) { int alt = key_pressed[KEY_LEFTALT] || key_pressed[KEY_RIGHTALT]; if (alt && key_code == KEY_L) return 1; - return key_code == KEY_HOME || key_code == KEY_MENU; + return key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_POWER || key_code == KEY_END; } int device_reboot_now(volatile char* key_pressed, int key_code) { From 841b2bf352cd4c8596d8062d97200390c1dec84c Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Mon, 15 Feb 2010 01:45:38 -0800 Subject: [PATCH 008/233] Add static flash_image for recovery --- mtdutils/Android.mk | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk index 57ab579..c96c702 100644 --- a/mtdutils/Android.mk +++ b/mtdutils/Android.mk @@ -20,5 +20,14 @@ LOCAL_STATIC_LIBRARIES := libmtdutils LOCAL_SHARED_LIBRARIES := libcutils libc include $(BUILD_EXECUTABLE) +include $(CLEAR_VARS) +LOCAL_SRC_FILES := flash_image.c +LOCAL_MODULE := recovery_flash_image +LOCAL_MODULE_TAGS := eng +LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc +LOCAL_MODULE_STEM := flash_image +LOCAL_FORCE_STATIC_EXECUTABLE := true +include $(BUILD_EXECUTABLE) + endif # TARGET_ARCH == arm endif # !TARGET_SIMULATOR From a3c2f735d7681aa1c0a927f2d45e08d86656dd09 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Fri, 19 Feb 2010 14:17:22 -0800 Subject: [PATCH 009/233] fix up back button and menu toggling --- default_recovery_ui.c | 11 ++++++++--- extendedcommands.h | 8 +++++++- recovery.c | 13 +++++++++++-- ui.c | 4 ++++ 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/default_recovery_ui.c b/default_recovery_ui.c index 991f376..2c39a78 100644 --- a/default_recovery_ui.c +++ b/default_recovery_ui.c @@ -18,6 +18,7 @@ #include "recovery_ui.h" #include "common.h" +#include "extendedcommands.h" char* MENU_HEADERS[] = { "Android system recovery utility", "", @@ -36,7 +37,8 @@ int device_toggle_display(volatile char* key_pressed, int key_code) { int alt = key_pressed[KEY_LEFTALT] || key_pressed[KEY_RIGHTALT]; if (alt && key_code == KEY_L) return 1; - return key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_POWER || key_code == KEY_END; + // allow toggling of the display if the correct key is pressed, and the display toggle is allowed or the display is currently off + return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_POWER || key_code == KEY_END); } int device_reboot_now(volatile char* key_pressed, int key_code) { @@ -57,9 +59,12 @@ int device_handle_key(int key_code, int visible) { case KEY_ENTER: case BTN_MOUSE: return SELECT_ITEM; - case KEY_BACKSPACE: + + case KEY_POWER: case KEY_END: - return GO_BACK; + case KEY_BACKSPACE: + if (!get_allow_toggle_display()) + return GO_BACK; } } diff --git a/extendedcommands.h b/extendedcommands.h index a4d4992..2729de2 100644 --- a/extendedcommands.h +++ b/extendedcommands.h @@ -8,4 +8,10 @@ void toggle_script_asserts(); void -show_choose_zip_menu(); \ No newline at end of file +show_choose_zip_menu(); + +int +get_allow_toggle_display(); + +int +ui_get_show_menu(); \ No newline at end of file diff --git a/recovery.c b/recovery.c index 7a12e10..e398393 100644 --- a/recovery.c +++ b/recovery.c @@ -49,6 +49,8 @@ static const struct option OPTIONS[] = { { NULL, 0, NULL, 0 }, }; +static int allow_display_toggle = 1; + static const char *COMMAND_FILE = "CACHE:recovery/command"; static const char *INTENT_FILE = "CACHE:recovery/intent"; static const char *LOG_FILE = "CACHE:recovery/log"; @@ -313,7 +315,7 @@ get_menu_selection(char** headers, char** items, int menu_only) { int selected = 0; int chosen_item = -1; - while (chosen_item < 0) { + while (chosen_item < 0 && chosen_item != GO_BACK) { int key = ui_wait_key(); int visible = ui_text_visible(); @@ -335,7 +337,8 @@ get_menu_selection(char** headers, char** items, int menu_only) { case NO_ACTION: break; case GO_BACK: - return GO_BACK; + chosen_item = GO_BACK; + break; } } else if (!menu_only) { chosen_item = action; @@ -394,7 +397,9 @@ prompt_and_wait() finish_recovery(NULL); ui_reset_progress(); + allow_display_toggle = 1; int chosen_item = get_menu_selection(headers, MENU_ITEMS, 0); + allow_display_toggle = 0; // device-specific code may take some action here. It may // return one of the core actions handled in the switch @@ -530,3 +535,7 @@ main(int argc, char **argv) reboot(RB_AUTOBOOT); return EXIT_SUCCESS; } + +int get_allow_toggle_display() { + return allow_display_toggle; +} \ No newline at end of file diff --git a/ui.c b/ui.c index 51df1fa..70ddc33 100644 --- a/ui.c +++ b/ui.c @@ -538,3 +538,7 @@ void ui_clear_key_queue() { key_queue_len = 0; pthread_mutex_unlock(&key_queue_mutex); } + +void ui_get_show_menu() { + return show_menu; +} From bf055bb1be6f1448af3c02ea88b505c32506c22e Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Fri, 19 Feb 2010 16:47:53 -0800 Subject: [PATCH 010/233] intetrate nandroid into recovery --- Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/Android.mk b/Android.mk index 66c30cc..60c2487 100644 --- a/Android.mk +++ b/Android.mk @@ -49,6 +49,7 @@ LOCAL_STATIC_LIBRARIES += libamend include $(BUILD_EXECUTABLE) include $(commands_recovery_local_path)/amend/Android.mk +include $(commands_recovery_local_path)/dump_image/Android.mk include $(commands_recovery_local_path)/minui/Android.mk include $(commands_recovery_local_path)/minzip/Android.mk include $(commands_recovery_local_path)/mtdutils/Android.mk From 3ab130fa9e213e40983472571da246e197930a9d Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Fri, 19 Feb 2010 16:50:36 -0800 Subject: [PATCH 011/233] add missing files --- dump_image/Android.mk | 15 + dump_image/dump_image.c | 136 +++++++++ dump_image/mtdutils.c | 600 ++++++++++++++++++++++++++++++++++++++++ dump_image/mtdutils.h | 55 ++++ res/nandroid-mobile.sh | 293 ++++++++++++++++++++ 5 files changed, 1099 insertions(+) create mode 100644 dump_image/Android.mk create mode 100644 dump_image/dump_image.c create mode 100644 dump_image/mtdutils.c create mode 100644 dump_image/mtdutils.h create mode 100755 res/nandroid-mobile.sh diff --git a/dump_image/Android.mk b/dump_image/Android.mk new file mode 100644 index 0000000..ab8ac38 --- /dev/null +++ b/dump_image/Android.mk @@ -0,0 +1,15 @@ +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_ARCH),arm) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_SRC_FILES := dump_image.c mtdutils.c ../mtdutils/mounts.c +LOCAL_MODULE := recovery_dump_image +LOCAL_MODULE_TAGS := eng +LOCAL_STATIC_LIBRARIES := libcutils libc +LOCAL_MODULE_STEM := dump_image +LOCAL_FORCE_STATIC_EXECUTABLE := true +include $(BUILD_EXECUTABLE) + +endif # TARGET_ARCH == arm +endif # !TARGET_SIMULATOR diff --git a/dump_image/dump_image.c b/dump_image/dump_image.c new file mode 100644 index 0000000..bc96abc --- /dev/null +++ b/dump_image/dump_image.c @@ -0,0 +1,136 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include "cutils/log.h" +#include "mtdutils.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif + +#define LOG_TAG "dump_image" + +#define BLOCK_SIZE 2048 +#define SPARE_SIZE (BLOCK_SIZE >> 5) + +void die(const char *msg, ...) { + int err = errno; + va_list args; + va_start(args, msg); + char buf[1024]; + vsnprintf(buf, sizeof(buf), msg, args); + va_end(args); + + if (err != 0) { + strlcat(buf, ": ", sizeof(buf)); + strlcat(buf, strerror(err), sizeof(buf)); + } + + fprintf(stderr, "%s\n", buf); + exit(1); +} + +/* Read a flash partition and write it to an image file. */ + +int main(int argc, char **argv) +{ + ssize_t (*read_func) (MtdReadContext *, char *, size_t); + MtdReadContext *in; + const MtdPartition *partition; + char buf[BLOCK_SIZE + SPARE_SIZE]; + size_t partition_size; + size_t read_size; + size_t total; + int fd; + int wrote; + int len; + + if (argc != 3) { + fprintf(stderr, "usage: %s partition file.img\n", argv[0]); + return 2; + } + + if (mtd_scan_partitions() <= 0) + die("error scanning partitions"); + + partition = mtd_find_partition_by_name(argv[1]); + if (partition == NULL) + die("can't find %s partition", argv[1]); + + if (mtd_partition_info(partition, &partition_size, NULL, NULL)) { + die("can't get info of partition %s", argv[1]); + } + + if (!strcmp(argv[2], "-")) { + fd = fileno(stdout); + } else { + fd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0666); + } + + if (fd < 0) + die("error opening %s", argv[2]); + + in = mtd_read_partition(partition); + if (in == NULL) { + close(fd); + unlink(argv[2]); + die("error opening %s: %s\n", argv[1], strerror(errno)); + } + + if (!strcmp(argv[1], "system") || + !strcmp(argv[1], "cache") || + !strcmp(argv[1], "userdata")) { + read_size = BLOCK_SIZE + SPARE_SIZE; + read_func = mtd_read_raw; + } else { + read_size = BLOCK_SIZE; + read_func = mtd_read_data; + } + + total = 0; + while ((len = read_func(in, buf, read_size)) > 0) { + wrote = write(fd, buf, len); + if (wrote != len) { + close(fd); + unlink(argv[2]); + die("error writing %s", argv[2]); + } + total += BLOCK_SIZE; + } + + if (total != partition_size) { + close(fd); + unlink(argv[2]); + die("error reading %s", argv[1]); + } + + mtd_read_close(in); + + if (close(fd)) { + unlink(argv[2]); + die("error closing %s", argv[2]); + } + + return 0; +} diff --git a/dump_image/mtdutils.c b/dump_image/mtdutils.c new file mode 100644 index 0000000..9ee7246 --- /dev/null +++ b/dump_image/mtdutils.c @@ -0,0 +1,600 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include // for _IOW, _IOR, mount() +#include +#include +#undef NDEBUG +#include + +#include "mtdutils.h" + +struct MtdPartition { + int device_index; + unsigned int size; + unsigned int erase_size; + char *name; +}; + +struct MtdReadContext { + const MtdPartition *partition; + char *buffer; + size_t read_size; + size_t consumed; + int fd; +}; + +struct MtdWriteContext { + const MtdPartition *partition; + char *buffer; + size_t stored; + int fd; +}; + +typedef struct { + MtdPartition *partitions; + int partitions_allocd; + int partition_count; +} MtdState; + +static MtdState g_mtd_state = { + NULL, // partitions + 0, // partitions_allocd + -1 // partition_count +}; + +#define MTD_PROC_FILENAME "/proc/mtd" + +int +mtd_scan_partitions() +{ + char buf[2048]; + const char *bufp; + int fd; + int i; + ssize_t nbytes; + + if (g_mtd_state.partitions == NULL) { + const int nump = 32; + MtdPartition *partitions = malloc(nump * sizeof(*partitions)); + if (partitions == NULL) { + errno = ENOMEM; + return -1; + } + g_mtd_state.partitions = partitions; + g_mtd_state.partitions_allocd = nump; + memset(partitions, 0, nump * sizeof(*partitions)); + } + g_mtd_state.partition_count = 0; + + /* Initialize all of the entries to make things easier later. + * (Lets us handle sparsely-numbered partitions, which + * may not even be possible.) + */ + for (i = 0; i < g_mtd_state.partitions_allocd; i++) { + MtdPartition *p = &g_mtd_state.partitions[i]; + if (p->name != NULL) { + free(p->name); + p->name = NULL; + } + p->device_index = -1; + } + + /* Open and read the file contents. + */ + fd = open(MTD_PROC_FILENAME, O_RDONLY); + if (fd < 0) { + goto bail; + } + nbytes = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (nbytes < 0) { + goto bail; + } + buf[nbytes] = '\0'; + + /* Parse the contents of the file, which looks like: + * + * # cat /proc/mtd + * dev: size erasesize name + * mtd0: 00080000 00020000 "bootloader" + * mtd1: 00400000 00020000 "mfg_and_gsm" + * mtd2: 00400000 00020000 "0000000c" + * mtd3: 00200000 00020000 "0000000d" + * mtd4: 04000000 00020000 "system" + * mtd5: 03280000 00020000 "userdata" + */ + bufp = buf; + while (nbytes > 0) { + int mtdnum, mtdsize, mtderasesize; + int matches; + char mtdname[64]; + mtdname[0] = '\0'; + mtdnum = -1; + + matches = sscanf(bufp, "mtd%d: %x %x \"%63[^\"]", + &mtdnum, &mtdsize, &mtderasesize, mtdname); + /* This will fail on the first line, which just contains + * column headers. + */ + if (matches == 4) { + MtdPartition *p = &g_mtd_state.partitions[mtdnum]; + p->device_index = mtdnum; + p->size = mtdsize; + p->erase_size = mtderasesize; + p->name = strdup(mtdname); + if (p->name == NULL) { + errno = ENOMEM; + goto bail; + } + g_mtd_state.partition_count++; + } + + /* Eat the line. + */ + while (nbytes > 0 && *bufp != '\n') { + bufp++; + nbytes--; + } + if (nbytes > 0) { + bufp++; + nbytes--; + } + } + + return g_mtd_state.partition_count; + +bail: + // keep "partitions" around so we can free the names on a rescan. + g_mtd_state.partition_count = -1; + return -1; +} + +const MtdPartition * +mtd_find_partition_by_name(const char *name) +{ + if (g_mtd_state.partitions != NULL) { + int i; + for (i = 0; i < g_mtd_state.partitions_allocd; i++) { + MtdPartition *p = &g_mtd_state.partitions[i]; + if (p->device_index >= 0 && p->name != NULL) { + if (strcmp(p->name, name) == 0) { + return p; + } + } + } + } + return NULL; +} + +int +mtd_mount_partition(const MtdPartition *partition, const char *mount_point, + const char *filesystem, int read_only) +{ + const unsigned long flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME; + char devname[64]; + int rv = -1; + + sprintf(devname, "/dev/block/mtdblock%d", partition->device_index); + if (!read_only) { + rv = mount(devname, mount_point, filesystem, flags, NULL); + } + if (read_only || rv < 0) { + rv = mount(devname, mount_point, filesystem, flags | MS_RDONLY, 0); + if (rv < 0) { + printf("Failed to mount %s on %s: %s\n", + devname, mount_point, strerror(errno)); + } else { + printf("Mount %s on %s read-only\n", devname, mount_point); + } + } +#if 1 //TODO: figure out why this is happening; remove include of stat.h + if (rv >= 0) { + /* For some reason, the x bits sometimes aren't set on the root + * of mounted volumes. + */ + struct stat st; + rv = stat(mount_point, &st); + if (rv < 0) { + return rv; + } + mode_t new_mode = st.st_mode | S_IXUSR | S_IXGRP | S_IXOTH; + if (new_mode != st.st_mode) { +printf("Fixing execute permissions for %s\n", mount_point); + rv = chmod(mount_point, new_mode); + if (rv < 0) { + printf("Couldn't fix permissions for %s: %s\n", + mount_point, strerror(errno)); + } + } + } +#endif + return rv; +} + +int +mtd_partition_info(const MtdPartition *partition, + size_t *total_size, size_t *erase_size, size_t *write_size) +{ + char mtddevname[32]; + sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index); + int fd = open(mtddevname, O_RDONLY); + if (fd < 0) return -1; + + struct mtd_info_user mtd_info; + int ret = ioctl(fd, MEMGETINFO, &mtd_info); + close(fd); + if (ret < 0) return -1; + + if (total_size != NULL) *total_size = mtd_info.size; + if (erase_size != NULL) *erase_size = mtd_info.erasesize; + if (write_size != NULL) *write_size = mtd_info.writesize; + return 0; +} + +MtdReadContext *mtd_read_partition(const MtdPartition *partition) +{ + MtdReadContext *ctx = (MtdReadContext*) malloc(sizeof(MtdReadContext)); + if (ctx == NULL) return NULL; + + ctx->buffer = malloc(partition->erase_size); + if (ctx->buffer == NULL) { + free(ctx); + return NULL; + } + + char mtddevname[32]; + sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index); + ctx->fd = open(mtddevname, O_RDONLY); + if (ctx->fd < 0) { + free(ctx); + free(ctx->buffer); + return NULL; + } + + ctx->partition = partition; + ctx->read_size = partition->erase_size; + ctx->consumed = ctx->read_size; + return ctx; +} + +static int read_block(const MtdReadContext *ctx, char *data) +{ + struct mtd_ecc_stats before, after; + off_t pos; + ssize_t size; + + if (ioctl(ctx->fd, ECCGETSTATS, &before)) { + fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno)); + return -1; + } + + pos = lseek(ctx->fd, 0, SEEK_CUR); + size = ctx->read_size; + + while (pos + size <= (int) ctx->partition->size) { + if (lseek(ctx->fd, pos, SEEK_SET) != pos || read(ctx->fd, data, size) != size) { + fprintf(stderr, "mtd: read error at 0x%08lx (%s)\n", + pos, strerror(errno)); + } else if (ioctl(ctx->fd, ECCGETSTATS, &after)) { + fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno)); + return -1; + } else if (after.failed != before.failed) { + fprintf(stderr, "mtd: ECC errors (%d soft, %d hard) at 0x%08lx\n", + after.corrected - before.corrected, + after.failed - before.failed, pos); + /* + * Reset error counts, so next read may succeed. + */ + before = after; + } else { + return 0; // Success! + } + + pos += ctx->read_size; + } + + errno = ENOSPC; + return -1; +} + +ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len) +{ + ssize_t read = 0; + while (read < (int) len) { + if (ctx->consumed < ctx->read_size) { + size_t avail = ctx->read_size - ctx->consumed; + size_t copy = len - read < avail ? len - read : avail; + memcpy(data + read, ctx->buffer + ctx->consumed, copy); + ctx->consumed += copy; + read += copy; + } + + // Read complete blocks directly into the user's buffer + while (ctx->consumed == ctx->read_size && + len - read >= ctx->read_size) { + if (read_block(ctx, data + read)) return -1; + read += ctx->read_size; + } + + // Read the next block into the buffer + if (ctx->consumed == ctx->read_size && read < (int) len) { + if (read_block(ctx, ctx->buffer)) return -1; + ctx->consumed = 0; + } + } + + return read; +} + +ssize_t mtd_read_raw(MtdReadContext *ctx, char *data, size_t len) +{ + static const int SPARE_SIZE = (2048 >> 5); + struct mtd_info_user mtd_info; + struct mtd_oob_buf oob_buf; + struct nand_ecclayout ecc_layout; + struct nand_oobfree *fp; + unsigned char ecc[SPARE_SIZE]; + char *src, *dst; + int i, n, ret; + +/* + * FIXME: These two ioctls should be cached in MtdReadContext. + */ + ret = ioctl(ctx->fd, MEMGETINFO, &mtd_info); + if (ret < 0) + return -1; + + ret = ioctl(ctx->fd, ECCGETLAYOUT, &ecc_layout); + if (ret < 0) + return -1; + + ctx->read_size = mtd_info.writesize; + ctx->consumed = ctx->read_size; + +/* + * Read next good data block. + */ + ret = read_block(ctx, data); + if (ret < 0) + return -1; + + dst = src = data + ctx->read_size; + +/* + * Read OOB data for last block read in read_block(). + */ + oob_buf.start = lseek(ctx->fd, 0, SEEK_CUR) - ctx->read_size; + oob_buf.length = mtd_info.oobsize; + oob_buf.ptr = (unsigned char *) src; + + ret = ioctl(ctx->fd, MEMREADOOB, &oob_buf); + if (ret < 0) + return -1; + +/* + * As yaffs and yaffs2 use mode MEM_OOB_AUTO, but mtdchar uses + * MEM_OOB_PLACE, copy the spare data down the hard way. + * + * Safe away ECC data: + */ + for (i = 0; i < ecc_layout.eccbytes; i++) { + ecc[i] = src[ecc_layout.eccpos[i]]; + } + for ( ; i < SPARE_SIZE; i++) { + ecc[i] = 0; + } + +/* + * Copy yaffs2 spare data down. + */ + n = ecc_layout.oobavail; + fp = &ecc_layout.oobfree[0]; + while (n) { + if (fp->offset) { + memmove(dst, src + fp->offset, fp->length); + } + dst += fp->length; + n -= fp->length; + ++fp; + } + +/* + * Restore ECC data behind spare data. + */ + memcpy(dst, ecc, (ctx->read_size >> 5) - ecc_layout.oobavail); + + return ctx->read_size + (ctx->read_size >> 5); +} + +void mtd_read_close(MtdReadContext *ctx) +{ + close(ctx->fd); + free(ctx->buffer); + free(ctx); +} + +MtdWriteContext *mtd_write_partition(const MtdPartition *partition) +{ + MtdWriteContext *ctx = (MtdWriteContext*) malloc(sizeof(MtdWriteContext)); + if (ctx == NULL) return NULL; + + ctx->buffer = malloc(partition->erase_size); + if (ctx->buffer == NULL) { + free(ctx); + return NULL; + } + + char mtddevname[32]; + sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index); + ctx->fd = open(mtddevname, O_RDWR); + if (ctx->fd < 0) { + free(ctx->buffer); + free(ctx); + return NULL; + } + + ctx->partition = partition; + ctx->stored = 0; + return ctx; +} + +static int write_block(const MtdPartition *partition, int fd, const char *data) +{ + off_t pos = lseek(fd, 0, SEEK_CUR); + if (pos == (off_t) -1) return 1; + + ssize_t size = partition->erase_size; + while (pos + size <= (int) partition->size) { + loff_t bpos = pos; + if (ioctl(fd, MEMGETBADBLOCK, &bpos) > 0) { + fprintf(stderr, "mtd: not writing bad block at 0x%08lx\n", pos); + pos += partition->erase_size; + continue; // Don't try to erase known factory-bad blocks. + } + + struct erase_info_user erase_info; + erase_info.start = pos; + erase_info.length = size; + int retry; + for (retry = 0; retry < 2; ++retry) { + if (ioctl(fd, MEMERASE, &erase_info) < 0) { + fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n", + pos, strerror(errno)); + continue; + } + if (lseek(fd, pos, SEEK_SET) != pos || + write(fd, data, size) != size) { + fprintf(stderr, "mtd: write error at 0x%08lx (%s)\n", + pos, strerror(errno)); + } + + char verify[size]; + if (lseek(fd, pos, SEEK_SET) != pos || + read(fd, verify, size) != size) { + fprintf(stderr, "mtd: re-read error at 0x%08lx (%s)\n", + pos, strerror(errno)); + continue; + } + if (memcmp(data, verify, size) != 0) { + fprintf(stderr, "mtd: verification error at 0x%08lx (%s)\n", + pos, strerror(errno)); + continue; + } + + if (retry > 0) { + fprintf(stderr, "mtd: wrote block after %d retries\n", retry); + } + return 0; // Success! + } + + // Try to erase it once more as we give up on this block + fprintf(stderr, "mtd: skipping write block at 0x%08lx\n", pos); + ioctl(fd, MEMERASE, &erase_info); + pos += partition->erase_size; + } + + // Ran out of space on the device + errno = ENOSPC; + return -1; +} + +ssize_t mtd_write_data(MtdWriteContext *ctx, const char *data, size_t len) +{ + size_t wrote = 0; + while (wrote < len) { + // Coalesce partial writes into complete blocks + if (ctx->stored > 0 || len - wrote < ctx->partition->erase_size) { + size_t avail = ctx->partition->erase_size - ctx->stored; + size_t copy = len - wrote < avail ? len - wrote : avail; + memcpy(ctx->buffer + ctx->stored, data + wrote, copy); + ctx->stored += copy; + wrote += copy; + } + + // If a complete block was accumulated, write it + if (ctx->stored == ctx->partition->erase_size) { + if (write_block(ctx->partition, ctx->fd, ctx->buffer)) return -1; + ctx->stored = 0; + } + + // Write complete blocks directly from the user's buffer + while (ctx->stored == 0 && len - wrote >= ctx->partition->erase_size) { + if (write_block(ctx->partition, ctx->fd, data + wrote)) return -1; + wrote += ctx->partition->erase_size; + } + } + + return wrote; +} + +off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks) +{ + // Zero-pad and write any pending data to get us to a block boundary + if (ctx->stored > 0) { + size_t zero = ctx->partition->erase_size - ctx->stored; + memset(ctx->buffer + ctx->stored, 0, zero); + if (write_block(ctx->partition, ctx->fd, ctx->buffer)) return -1; + ctx->stored = 0; + } + + off_t pos = lseek(ctx->fd, 0, SEEK_CUR); + if ((off_t) pos == (off_t) -1) return pos; + + const int total = (ctx->partition->size - pos) / ctx->partition->erase_size; + if (blocks < 0) blocks = total; + if (blocks > total) { + errno = ENOSPC; + return -1; + } + + // Erase the specified number of blocks + while (blocks-- > 0) { + loff_t bpos = pos; + if (ioctl(ctx->fd, MEMGETBADBLOCK, &bpos) > 0) { + fprintf(stderr, "mtd: not erasing bad block at 0x%08lx\n", pos); + pos += ctx->partition->erase_size; + continue; // Don't try to erase known factory-bad blocks. + } + + struct erase_info_user erase_info; + erase_info.start = pos; + erase_info.length = ctx->partition->erase_size; + if (ioctl(ctx->fd, MEMERASE, &erase_info) < 0) { + fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos); + } + pos += ctx->partition->erase_size; + } + + return pos; +} + +int mtd_write_close(MtdWriteContext *ctx) +{ + int r = 0; + // Make sure any pending data gets written + if (mtd_erase_blocks(ctx, 0) == (off_t) -1) r = -1; + if (close(ctx->fd)) r = -1; + free(ctx->buffer); + free(ctx); + return r; +} diff --git a/dump_image/mtdutils.h b/dump_image/mtdutils.h new file mode 100644 index 0000000..4a35543 --- /dev/null +++ b/dump_image/mtdutils.h @@ -0,0 +1,55 @@ +/* + * 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 MTDUTILS_H_ +#define MTDUTILS_H_ + +#include // for size_t, etc. + +typedef struct MtdPartition MtdPartition; + +int mtd_scan_partitions(void); + +const MtdPartition *mtd_find_partition_by_name(const char *name); + +/* mount_point is like "/system" + * filesystem is like "yaffs2" + */ +int mtd_mount_partition(const MtdPartition *partition, const char *mount_point, + const char *filesystem, int read_only); + +/* get the partition and the minimum erase/write block size. NULL is ok. + */ +int mtd_partition_info(const MtdPartition *partition, + size_t *total_size, size_t *erase_size, size_t *write_size); + +/* read or write raw data from a partition, starting at the beginning. + * skips bad blocks as best we can. + */ +typedef struct MtdReadContext MtdReadContext; +typedef struct MtdWriteContext MtdWriteContext; + +MtdReadContext *mtd_read_partition(const MtdPartition *); +ssize_t mtd_read_data(MtdReadContext *, char *data, size_t data_len); +ssize_t mtd_read_raw(MtdReadContext *, char *data, size_t data_len); +void mtd_read_close(MtdReadContext *); + +MtdWriteContext *mtd_write_partition(const MtdPartition *); +ssize_t mtd_write_data(MtdWriteContext *, const char *data, size_t data_len); +off_t mtd_erase_blocks(MtdWriteContext *, int blocks); /* 0 ok, -1 for all */ +int mtd_write_close(MtdWriteContext *); + +#endif // MTDUTILS_H_ diff --git a/res/nandroid-mobile.sh b/res/nandroid-mobile.sh new file mode 100755 index 0000000..9a9a824 --- /dev/null +++ b/res/nandroid-mobile.sh @@ -0,0 +1,293 @@ +#!/sbin/sh + +# nandroid v2.1 - an Android backup tool for the G1 by infernix and brainaid + +# Requirements: + +# - a modded android in recovery mode (JF 1.3 will work by default) +# - adb shell as root in recovery mode if not using a pre-made recovery image +# - busybox in recovery mode +# - dump_image-arm-uclibc compiled and in path on phone +# - mkyaffs2image-arm-uclibc compiled and installed in path on phone + +# Reference data: + +# dev: size erasesize name +#mtd0: 00040000 00020000 "misc" +#mtd1: 00500000 00020000 "recovery" +#mtd2: 00280000 00020000 "boot" +#mtd3: 04380000 00020000 "system" +#mtd4: 04380000 00020000 "cache" +#mtd5: 04ac0000 00020000 "userdata" +#mtd6 is everything, dump splash1 with: dd if=/dev/mtd/mtd6ro of=/sdcard/splash1.img skip=19072 bs=2048 count=150 + +# We don't dump misc or cache because they do not contain any useful data that we are aware of at this time. + +# Logical steps (v2.1): +# +# 0. test for a target dir and the various tools needed, if not found then exit with error. +# 1. check "adb devices" for a device in recovery mode. set DEVICEID variable to the device ID. abort when not found. +# 2. mount system and data partitions read-only, set up adb portforward and create destdir +# 3. check free space on /cache, exit if less blocks than 20MB free +# 4. push required tools to device in /cache +# 5 for partitions boot recovery misc: +# 5a get md5sum for content of current partition *on the device* (no data transfered) +# 5b while MD5sum comparison is incorrect (always is the first time): +# 5b1 dump current partition to a netcat session +# 5b2 start local netcat to dump image to current dir +# 5b3 compare md5sums of dumped data with dump in current dir. if correct, contine, else restart the loop (6b1) +# 6 for partitions system data: +# 6a get md5sum for tar of content of current partition *on the device* (no data transfered) +# 6b while MD5sum comparison is incorrect (always is the first time): +# 6b1 tar current partition to a netcat session +# 6b2 start local netcat to dump tar to current dir +# 6b3 compare md5sums of dumped data with dump in current dir. if correct, contine, else restart the loop (6b1) +# 6c if i'm running as root: +# 6c1 create a temp dir using either tempdir command or the deviceid in /tmp +# 6c2 extract tar to tempdir +# 6c3 invoke mkyaffs2image to create the img +# 6c4 clean up +# 7. remove tools from device /cache +# 8. umount system and data on device +# 9. print success. + + +DEVICEID=foo +RECOVERY=foo + +echo "nandroid-mobile v2.1" + + +if [ "$1" == "" ]; then + echo "Usage: $0 {backup|restore} [/path/to/nandroid/backup/]" + echo "- backup will store a full system backup on /sdcard/nandroid/$DEVICEID" + echo "- restore path will restore the last made backup for boot, system, recovery and data" + exit 0 +fi + +case $1 in + backup) + mkyaffs2image=`which mkyaffs2image` + if [ "$mkyaffs2image" == "" ]; then + mkyaffs2image=`which mkyaffs2image-arm-uclibc` + if [ "$mkyaffs2image" == "" ]; then + echo "error: mkyaffs2image or mkyaffs2image-arm-uclibc not found in path" + exit 1 + fi + fi + dump_image=`which dump_image` + if [ "$dump_image" == "" ]; then + dump_image=`which dump_image-arm-uclibc` + if [ "$dump_image" == "" ]; then + echo "error: dump_image or dump_image-arm-uclibc not found in path" + exit 1 + fi + fi + break + ;; + restore) + flash_image=`which flash_image` + if [ "$flash_image" == "" ]; then + flash_image=`which flash_image-arm-uclibc` + if [ "$flash_image" == "" ]; then + echo "error: flash_image or flash_image-arm-uclibc not found in path" + exit 1 + fi + fi + break + ;; +esac + +# 1 +DEVICEID=`cat /proc/cmdline | sed "s/.*serialno=//" | cut -d" " -f1` +RECOVERY=`cat /proc/cmdline | grep "androidboot.mode=recovery"` +if [ "$RECOVERY" == "foo" ]; then + echo "error: not running in recovery mode, aborting" + exit 1 +fi +if [ "$DEVICEID" == "foo" ]; then + echo "error: device id not found in /proc/cmdline, aborting" + exit 1 +fi +if [ ! "`id -u 2>/dev/null`" == "0" ]; then + if [ "`whoami 2>&1 | grep 'uid 0'`" == "" ]; then + echo "error: must run as root, aborting" + exit 1 + fi +fi + + +case $1 in + restore) + ENERGY=`cat /sys/class/power_supply/battery/capacity` + if [ "`cat /sys/class/power_supply/battery/status`" == "Charging" ]; then + ENERGY=100 + fi + if [ ! $ENERGY -ge 30 ]; then + echo "Error: not enough battery power" + echo "Connect charger or USB power and try again" + exit 1 + fi + RESTOREPATH=$2 + if [ ! -f $RESTOREPATH/nandroid.md5 ]; then + echo "error: $RESTOREPATH/nandroid.md5 not found, cannot verify backup data" + exit 1 + fi + umount /system 2>/dev/null + umount /data 2>/dev/null + if [ ! "`mount | grep data`" == "" ]; then + echo "error: unable to umount /data, aborting" + exit 1 + fi + if [ ! "`mount | grep system`" == "" ]; then + echo "error: unable to umount /system, aborting" + exit 1 + fi + + echo "Verifying backup images..." + CWD=$PWD + cd $RESTOREPATH + md5sum -c nandroid.md5 + if [ $? -eq 1 ]; then + echo "error: md5sum mismatch, aborting" + exit 1 + fi + for image in boot recovery; do + echo "Flashing $image..." + $flash_image $image $image.img + done + echo "Flashing system and data not currently supported" + echo "Restore done" + exit 0 + ;; + backup) + break + ;; + *) + echo "Usage: $0 {backup|restore} [/path/to/nandroid/backup/]" + echo "- backup will store a full system backup on /sdcard/nandroid/$DEVICEID" + echo "- restore path will restore the last made backup for boot, system, recovery and data" + exit 1 + ;; +esac + +# 2. +echo "mounting system and data read-only, sdcard read-write" +umount /system 2>/dev/null +umount /data 2>/dev/null +umount /sdcard 2>/dev/null +mount -o ro /system || FAIL=1 +mount -o ro /data || FAIL=2 +mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=3 +case $FAIL in + 1) echo "Error mounting system read-only"; umount /system /data /sdcard; exit 1;; + 2) echo "Error mounting data read-only"; umount /system /data /sdcard; exit 1;; + 3) echo "Error mounting sdcard read-write"; umount /system /data /sdcard; exit 1;; +esac + +TIMESTAMP="`date +%Y%m%d-%H%M`" +DESTDIR="/sdcard/nandroid/$DEVICEID/$TIMESTAMP" +if [ ! -d $DESTDIR ]; then + mkdir -p $DESTDIR + if [ ! -d $DESTDIR ]; then + echo "error: cannot create $DESTDIR" + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + exit 1 + fi +else + touch $DESTDIR/.nandroidwritable + if [ ! -e $DESTDIR/.nandroidwritable ]; then + echo "error: cannot write to $DESTDIR" + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + exit 1 + fi + rm $DESTDIR/.nandroidwritable +fi + +# 3. +echo "checking free space on sdcard" +FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" +# we need about 130MB for the dump +if [ $FREEBLOCKS -le 130000 ]; then + echo "error: not enough free space available on sdcard (need 130mb), aborting." + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + exit 1 +fi + + + +if [ -e /dev/mtd/mtd6ro ]; then + echo -n "Dumping splash1 from device over tcp to $DESTDIR/splash1.img..." + dd if=/dev/mtd/mtd6ro of=$DESTDIR/splash1.img skip=19072 bs=2048 count=150 2>/dev/null + echo "done" + sleep 1s + echo -n "Dumping splash2 from device over tcp to $DESTDIR/splash2.img..." + dd if=/dev/mtd/mtd6ro of=$DESTDIR/splash2.img skip=19456 bs=2048 count=150 2>/dev/null + echo "done" +fi + + +# 5. +for image in boot recovery misc; do + # 5a + DEVICEMD5=`$dump_image $image - | md5sum | awk '{ print $1 }'` + sleep 1s + MD5RESULT=1 + # 5b + echo -n "Dumping $image to $DESTDIR/$image.img..." + ATTEMPT=0 + while [ $MD5RESULT -eq 1 ]; do + let ATTEMPT=$ATTEMPT+1 + # 5b1 + $dump_image $image $DESTDIR/$image.img + sync + # 5b3 + echo "${DEVICEMD5} $DESTDIR/$image.img" | md5sum -c -s - + if [ $? -eq 1 ]; then + true + else + MD5RESULT=0 + fi + if [ "$ATTEMPT" == "5" ]; then + echo "fatal error while trying to dump $image, aborting" + umount /system + umount /data + umount /sdcard + exit 1 + fi + done + echo "done" +done + +# 6 +for image in system data cache; do + # 6a + echo -n "Dumping $image to $DESTDIR/$image.img..." + $mkyaffs2image /$image $DESTDIR/$image.img + sync + echo "done" +done + + +# 7. +echo -n "generating md5sum file..." +CWD=$PWD +cd $DESTDIR +md5sum *img > nandroid.md5 +cd $CWD +echo "done" + +# 8. +echo "unmounting system, data and sdcard" +umount /system +umount /data +umount /sdcard + +# 9. +echo "Backup successful." From 8ce0be4956f8a942f06644a00347769126b4976e Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Sat, 20 Feb 2010 15:59:06 -0800 Subject: [PATCH 012/233] nearly working nandroid, built against libc --- Android.mk | 2 +- dump_image/Android.mk | 15 - dump_image/mtdutils.c | 600 --------- dump_image/mtdutils.h | 55 - mtdutils/Android.mk | 19 + {dump_image => mtdutils}/dump_image.c | 0 mtdutils/mtdutils.c | 82 ++ mtdutils/mtdutils.h | 2 + nandroid/Android.mk | 24 + nandroid/nandroid-mobile.sh | 1790 +++++++++++++++++++++++++ nandroid/unyaffs.c | 118 ++ nandroid/unyaffs.h | 144 ++ res/nandroid-mobile.sh | 293 ---- 13 files changed, 2180 insertions(+), 964 deletions(-) delete mode 100644 dump_image/Android.mk delete mode 100644 dump_image/mtdutils.c delete mode 100644 dump_image/mtdutils.h rename {dump_image => mtdutils}/dump_image.c (100%) create mode 100644 nandroid/Android.mk create mode 100755 nandroid/nandroid-mobile.sh create mode 100644 nandroid/unyaffs.c create mode 100644 nandroid/unyaffs.h delete mode 100755 res/nandroid-mobile.sh diff --git a/Android.mk b/Android.mk index 60c2487..2afb46f 100644 --- a/Android.mk +++ b/Android.mk @@ -49,7 +49,7 @@ LOCAL_STATIC_LIBRARIES += libamend include $(BUILD_EXECUTABLE) include $(commands_recovery_local_path)/amend/Android.mk -include $(commands_recovery_local_path)/dump_image/Android.mk +include $(commands_recovery_local_path)/nandroid/Android.mk include $(commands_recovery_local_path)/minui/Android.mk include $(commands_recovery_local_path)/minzip/Android.mk include $(commands_recovery_local_path)/mtdutils/Android.mk diff --git a/dump_image/Android.mk b/dump_image/Android.mk deleted file mode 100644 index ab8ac38..0000000 --- a/dump_image/Android.mk +++ /dev/null @@ -1,15 +0,0 @@ -ifneq ($(TARGET_SIMULATOR),true) -ifeq ($(TARGET_ARCH),arm) - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) -LOCAL_SRC_FILES := dump_image.c mtdutils.c ../mtdutils/mounts.c -LOCAL_MODULE := recovery_dump_image -LOCAL_MODULE_TAGS := eng -LOCAL_STATIC_LIBRARIES := libcutils libc -LOCAL_MODULE_STEM := dump_image -LOCAL_FORCE_STATIC_EXECUTABLE := true -include $(BUILD_EXECUTABLE) - -endif # TARGET_ARCH == arm -endif # !TARGET_SIMULATOR diff --git a/dump_image/mtdutils.c b/dump_image/mtdutils.c deleted file mode 100644 index 9ee7246..0000000 --- a/dump_image/mtdutils.c +++ /dev/null @@ -1,600 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include // for _IOW, _IOR, mount() -#include -#include -#undef NDEBUG -#include - -#include "mtdutils.h" - -struct MtdPartition { - int device_index; - unsigned int size; - unsigned int erase_size; - char *name; -}; - -struct MtdReadContext { - const MtdPartition *partition; - char *buffer; - size_t read_size; - size_t consumed; - int fd; -}; - -struct MtdWriteContext { - const MtdPartition *partition; - char *buffer; - size_t stored; - int fd; -}; - -typedef struct { - MtdPartition *partitions; - int partitions_allocd; - int partition_count; -} MtdState; - -static MtdState g_mtd_state = { - NULL, // partitions - 0, // partitions_allocd - -1 // partition_count -}; - -#define MTD_PROC_FILENAME "/proc/mtd" - -int -mtd_scan_partitions() -{ - char buf[2048]; - const char *bufp; - int fd; - int i; - ssize_t nbytes; - - if (g_mtd_state.partitions == NULL) { - const int nump = 32; - MtdPartition *partitions = malloc(nump * sizeof(*partitions)); - if (partitions == NULL) { - errno = ENOMEM; - return -1; - } - g_mtd_state.partitions = partitions; - g_mtd_state.partitions_allocd = nump; - memset(partitions, 0, nump * sizeof(*partitions)); - } - g_mtd_state.partition_count = 0; - - /* Initialize all of the entries to make things easier later. - * (Lets us handle sparsely-numbered partitions, which - * may not even be possible.) - */ - for (i = 0; i < g_mtd_state.partitions_allocd; i++) { - MtdPartition *p = &g_mtd_state.partitions[i]; - if (p->name != NULL) { - free(p->name); - p->name = NULL; - } - p->device_index = -1; - } - - /* Open and read the file contents. - */ - fd = open(MTD_PROC_FILENAME, O_RDONLY); - if (fd < 0) { - goto bail; - } - nbytes = read(fd, buf, sizeof(buf) - 1); - close(fd); - if (nbytes < 0) { - goto bail; - } - buf[nbytes] = '\0'; - - /* Parse the contents of the file, which looks like: - * - * # cat /proc/mtd - * dev: size erasesize name - * mtd0: 00080000 00020000 "bootloader" - * mtd1: 00400000 00020000 "mfg_and_gsm" - * mtd2: 00400000 00020000 "0000000c" - * mtd3: 00200000 00020000 "0000000d" - * mtd4: 04000000 00020000 "system" - * mtd5: 03280000 00020000 "userdata" - */ - bufp = buf; - while (nbytes > 0) { - int mtdnum, mtdsize, mtderasesize; - int matches; - char mtdname[64]; - mtdname[0] = '\0'; - mtdnum = -1; - - matches = sscanf(bufp, "mtd%d: %x %x \"%63[^\"]", - &mtdnum, &mtdsize, &mtderasesize, mtdname); - /* This will fail on the first line, which just contains - * column headers. - */ - if (matches == 4) { - MtdPartition *p = &g_mtd_state.partitions[mtdnum]; - p->device_index = mtdnum; - p->size = mtdsize; - p->erase_size = mtderasesize; - p->name = strdup(mtdname); - if (p->name == NULL) { - errno = ENOMEM; - goto bail; - } - g_mtd_state.partition_count++; - } - - /* Eat the line. - */ - while (nbytes > 0 && *bufp != '\n') { - bufp++; - nbytes--; - } - if (nbytes > 0) { - bufp++; - nbytes--; - } - } - - return g_mtd_state.partition_count; - -bail: - // keep "partitions" around so we can free the names on a rescan. - g_mtd_state.partition_count = -1; - return -1; -} - -const MtdPartition * -mtd_find_partition_by_name(const char *name) -{ - if (g_mtd_state.partitions != NULL) { - int i; - for (i = 0; i < g_mtd_state.partitions_allocd; i++) { - MtdPartition *p = &g_mtd_state.partitions[i]; - if (p->device_index >= 0 && p->name != NULL) { - if (strcmp(p->name, name) == 0) { - return p; - } - } - } - } - return NULL; -} - -int -mtd_mount_partition(const MtdPartition *partition, const char *mount_point, - const char *filesystem, int read_only) -{ - const unsigned long flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME; - char devname[64]; - int rv = -1; - - sprintf(devname, "/dev/block/mtdblock%d", partition->device_index); - if (!read_only) { - rv = mount(devname, mount_point, filesystem, flags, NULL); - } - if (read_only || rv < 0) { - rv = mount(devname, mount_point, filesystem, flags | MS_RDONLY, 0); - if (rv < 0) { - printf("Failed to mount %s on %s: %s\n", - devname, mount_point, strerror(errno)); - } else { - printf("Mount %s on %s read-only\n", devname, mount_point); - } - } -#if 1 //TODO: figure out why this is happening; remove include of stat.h - if (rv >= 0) { - /* For some reason, the x bits sometimes aren't set on the root - * of mounted volumes. - */ - struct stat st; - rv = stat(mount_point, &st); - if (rv < 0) { - return rv; - } - mode_t new_mode = st.st_mode | S_IXUSR | S_IXGRP | S_IXOTH; - if (new_mode != st.st_mode) { -printf("Fixing execute permissions for %s\n", mount_point); - rv = chmod(mount_point, new_mode); - if (rv < 0) { - printf("Couldn't fix permissions for %s: %s\n", - mount_point, strerror(errno)); - } - } - } -#endif - return rv; -} - -int -mtd_partition_info(const MtdPartition *partition, - size_t *total_size, size_t *erase_size, size_t *write_size) -{ - char mtddevname[32]; - sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index); - int fd = open(mtddevname, O_RDONLY); - if (fd < 0) return -1; - - struct mtd_info_user mtd_info; - int ret = ioctl(fd, MEMGETINFO, &mtd_info); - close(fd); - if (ret < 0) return -1; - - if (total_size != NULL) *total_size = mtd_info.size; - if (erase_size != NULL) *erase_size = mtd_info.erasesize; - if (write_size != NULL) *write_size = mtd_info.writesize; - return 0; -} - -MtdReadContext *mtd_read_partition(const MtdPartition *partition) -{ - MtdReadContext *ctx = (MtdReadContext*) malloc(sizeof(MtdReadContext)); - if (ctx == NULL) return NULL; - - ctx->buffer = malloc(partition->erase_size); - if (ctx->buffer == NULL) { - free(ctx); - return NULL; - } - - char mtddevname[32]; - sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index); - ctx->fd = open(mtddevname, O_RDONLY); - if (ctx->fd < 0) { - free(ctx); - free(ctx->buffer); - return NULL; - } - - ctx->partition = partition; - ctx->read_size = partition->erase_size; - ctx->consumed = ctx->read_size; - return ctx; -} - -static int read_block(const MtdReadContext *ctx, char *data) -{ - struct mtd_ecc_stats before, after; - off_t pos; - ssize_t size; - - if (ioctl(ctx->fd, ECCGETSTATS, &before)) { - fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno)); - return -1; - } - - pos = lseek(ctx->fd, 0, SEEK_CUR); - size = ctx->read_size; - - while (pos + size <= (int) ctx->partition->size) { - if (lseek(ctx->fd, pos, SEEK_SET) != pos || read(ctx->fd, data, size) != size) { - fprintf(stderr, "mtd: read error at 0x%08lx (%s)\n", - pos, strerror(errno)); - } else if (ioctl(ctx->fd, ECCGETSTATS, &after)) { - fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno)); - return -1; - } else if (after.failed != before.failed) { - fprintf(stderr, "mtd: ECC errors (%d soft, %d hard) at 0x%08lx\n", - after.corrected - before.corrected, - after.failed - before.failed, pos); - /* - * Reset error counts, so next read may succeed. - */ - before = after; - } else { - return 0; // Success! - } - - pos += ctx->read_size; - } - - errno = ENOSPC; - return -1; -} - -ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len) -{ - ssize_t read = 0; - while (read < (int) len) { - if (ctx->consumed < ctx->read_size) { - size_t avail = ctx->read_size - ctx->consumed; - size_t copy = len - read < avail ? len - read : avail; - memcpy(data + read, ctx->buffer + ctx->consumed, copy); - ctx->consumed += copy; - read += copy; - } - - // Read complete blocks directly into the user's buffer - while (ctx->consumed == ctx->read_size && - len - read >= ctx->read_size) { - if (read_block(ctx, data + read)) return -1; - read += ctx->read_size; - } - - // Read the next block into the buffer - if (ctx->consumed == ctx->read_size && read < (int) len) { - if (read_block(ctx, ctx->buffer)) return -1; - ctx->consumed = 0; - } - } - - return read; -} - -ssize_t mtd_read_raw(MtdReadContext *ctx, char *data, size_t len) -{ - static const int SPARE_SIZE = (2048 >> 5); - struct mtd_info_user mtd_info; - struct mtd_oob_buf oob_buf; - struct nand_ecclayout ecc_layout; - struct nand_oobfree *fp; - unsigned char ecc[SPARE_SIZE]; - char *src, *dst; - int i, n, ret; - -/* - * FIXME: These two ioctls should be cached in MtdReadContext. - */ - ret = ioctl(ctx->fd, MEMGETINFO, &mtd_info); - if (ret < 0) - return -1; - - ret = ioctl(ctx->fd, ECCGETLAYOUT, &ecc_layout); - if (ret < 0) - return -1; - - ctx->read_size = mtd_info.writesize; - ctx->consumed = ctx->read_size; - -/* - * Read next good data block. - */ - ret = read_block(ctx, data); - if (ret < 0) - return -1; - - dst = src = data + ctx->read_size; - -/* - * Read OOB data for last block read in read_block(). - */ - oob_buf.start = lseek(ctx->fd, 0, SEEK_CUR) - ctx->read_size; - oob_buf.length = mtd_info.oobsize; - oob_buf.ptr = (unsigned char *) src; - - ret = ioctl(ctx->fd, MEMREADOOB, &oob_buf); - if (ret < 0) - return -1; - -/* - * As yaffs and yaffs2 use mode MEM_OOB_AUTO, but mtdchar uses - * MEM_OOB_PLACE, copy the spare data down the hard way. - * - * Safe away ECC data: - */ - for (i = 0; i < ecc_layout.eccbytes; i++) { - ecc[i] = src[ecc_layout.eccpos[i]]; - } - for ( ; i < SPARE_SIZE; i++) { - ecc[i] = 0; - } - -/* - * Copy yaffs2 spare data down. - */ - n = ecc_layout.oobavail; - fp = &ecc_layout.oobfree[0]; - while (n) { - if (fp->offset) { - memmove(dst, src + fp->offset, fp->length); - } - dst += fp->length; - n -= fp->length; - ++fp; - } - -/* - * Restore ECC data behind spare data. - */ - memcpy(dst, ecc, (ctx->read_size >> 5) - ecc_layout.oobavail); - - return ctx->read_size + (ctx->read_size >> 5); -} - -void mtd_read_close(MtdReadContext *ctx) -{ - close(ctx->fd); - free(ctx->buffer); - free(ctx); -} - -MtdWriteContext *mtd_write_partition(const MtdPartition *partition) -{ - MtdWriteContext *ctx = (MtdWriteContext*) malloc(sizeof(MtdWriteContext)); - if (ctx == NULL) return NULL; - - ctx->buffer = malloc(partition->erase_size); - if (ctx->buffer == NULL) { - free(ctx); - return NULL; - } - - char mtddevname[32]; - sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index); - ctx->fd = open(mtddevname, O_RDWR); - if (ctx->fd < 0) { - free(ctx->buffer); - free(ctx); - return NULL; - } - - ctx->partition = partition; - ctx->stored = 0; - return ctx; -} - -static int write_block(const MtdPartition *partition, int fd, const char *data) -{ - off_t pos = lseek(fd, 0, SEEK_CUR); - if (pos == (off_t) -1) return 1; - - ssize_t size = partition->erase_size; - while (pos + size <= (int) partition->size) { - loff_t bpos = pos; - if (ioctl(fd, MEMGETBADBLOCK, &bpos) > 0) { - fprintf(stderr, "mtd: not writing bad block at 0x%08lx\n", pos); - pos += partition->erase_size; - continue; // Don't try to erase known factory-bad blocks. - } - - struct erase_info_user erase_info; - erase_info.start = pos; - erase_info.length = size; - int retry; - for (retry = 0; retry < 2; ++retry) { - if (ioctl(fd, MEMERASE, &erase_info) < 0) { - fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n", - pos, strerror(errno)); - continue; - } - if (lseek(fd, pos, SEEK_SET) != pos || - write(fd, data, size) != size) { - fprintf(stderr, "mtd: write error at 0x%08lx (%s)\n", - pos, strerror(errno)); - } - - char verify[size]; - if (lseek(fd, pos, SEEK_SET) != pos || - read(fd, verify, size) != size) { - fprintf(stderr, "mtd: re-read error at 0x%08lx (%s)\n", - pos, strerror(errno)); - continue; - } - if (memcmp(data, verify, size) != 0) { - fprintf(stderr, "mtd: verification error at 0x%08lx (%s)\n", - pos, strerror(errno)); - continue; - } - - if (retry > 0) { - fprintf(stderr, "mtd: wrote block after %d retries\n", retry); - } - return 0; // Success! - } - - // Try to erase it once more as we give up on this block - fprintf(stderr, "mtd: skipping write block at 0x%08lx\n", pos); - ioctl(fd, MEMERASE, &erase_info); - pos += partition->erase_size; - } - - // Ran out of space on the device - errno = ENOSPC; - return -1; -} - -ssize_t mtd_write_data(MtdWriteContext *ctx, const char *data, size_t len) -{ - size_t wrote = 0; - while (wrote < len) { - // Coalesce partial writes into complete blocks - if (ctx->stored > 0 || len - wrote < ctx->partition->erase_size) { - size_t avail = ctx->partition->erase_size - ctx->stored; - size_t copy = len - wrote < avail ? len - wrote : avail; - memcpy(ctx->buffer + ctx->stored, data + wrote, copy); - ctx->stored += copy; - wrote += copy; - } - - // If a complete block was accumulated, write it - if (ctx->stored == ctx->partition->erase_size) { - if (write_block(ctx->partition, ctx->fd, ctx->buffer)) return -1; - ctx->stored = 0; - } - - // Write complete blocks directly from the user's buffer - while (ctx->stored == 0 && len - wrote >= ctx->partition->erase_size) { - if (write_block(ctx->partition, ctx->fd, data + wrote)) return -1; - wrote += ctx->partition->erase_size; - } - } - - return wrote; -} - -off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks) -{ - // Zero-pad and write any pending data to get us to a block boundary - if (ctx->stored > 0) { - size_t zero = ctx->partition->erase_size - ctx->stored; - memset(ctx->buffer + ctx->stored, 0, zero); - if (write_block(ctx->partition, ctx->fd, ctx->buffer)) return -1; - ctx->stored = 0; - } - - off_t pos = lseek(ctx->fd, 0, SEEK_CUR); - if ((off_t) pos == (off_t) -1) return pos; - - const int total = (ctx->partition->size - pos) / ctx->partition->erase_size; - if (blocks < 0) blocks = total; - if (blocks > total) { - errno = ENOSPC; - return -1; - } - - // Erase the specified number of blocks - while (blocks-- > 0) { - loff_t bpos = pos; - if (ioctl(ctx->fd, MEMGETBADBLOCK, &bpos) > 0) { - fprintf(stderr, "mtd: not erasing bad block at 0x%08lx\n", pos); - pos += ctx->partition->erase_size; - continue; // Don't try to erase known factory-bad blocks. - } - - struct erase_info_user erase_info; - erase_info.start = pos; - erase_info.length = ctx->partition->erase_size; - if (ioctl(ctx->fd, MEMERASE, &erase_info) < 0) { - fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos); - } - pos += ctx->partition->erase_size; - } - - return pos; -} - -int mtd_write_close(MtdWriteContext *ctx) -{ - int r = 0; - // Make sure any pending data gets written - if (mtd_erase_blocks(ctx, 0) == (off_t) -1) r = -1; - if (close(ctx->fd)) r = -1; - free(ctx->buffer); - free(ctx); - return r; -} diff --git a/dump_image/mtdutils.h b/dump_image/mtdutils.h deleted file mode 100644 index 4a35543..0000000 --- a/dump_image/mtdutils.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MTDUTILS_H_ -#define MTDUTILS_H_ - -#include // for size_t, etc. - -typedef struct MtdPartition MtdPartition; - -int mtd_scan_partitions(void); - -const MtdPartition *mtd_find_partition_by_name(const char *name); - -/* mount_point is like "/system" - * filesystem is like "yaffs2" - */ -int mtd_mount_partition(const MtdPartition *partition, const char *mount_point, - const char *filesystem, int read_only); - -/* get the partition and the minimum erase/write block size. NULL is ok. - */ -int mtd_partition_info(const MtdPartition *partition, - size_t *total_size, size_t *erase_size, size_t *write_size); - -/* read or write raw data from a partition, starting at the beginning. - * skips bad blocks as best we can. - */ -typedef struct MtdReadContext MtdReadContext; -typedef struct MtdWriteContext MtdWriteContext; - -MtdReadContext *mtd_read_partition(const MtdPartition *); -ssize_t mtd_read_data(MtdReadContext *, char *data, size_t data_len); -ssize_t mtd_read_raw(MtdReadContext *, char *data, size_t data_len); -void mtd_read_close(MtdReadContext *); - -MtdWriteContext *mtd_write_partition(const MtdPartition *); -ssize_t mtd_write_data(MtdWriteContext *, const char *data, size_t data_len); -off_t mtd_erase_blocks(MtdWriteContext *, int blocks); /* 0 ok, -1 for all */ -int mtd_write_close(MtdWriteContext *); - -#endif // MTDUTILS_H_ diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk index c96c702..b6a5ae4 100644 --- a/mtdutils/Android.mk +++ b/mtdutils/Android.mk @@ -27,6 +27,25 @@ LOCAL_MODULE_TAGS := eng LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc LOCAL_MODULE_STEM := flash_image LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +ADDITIONAL_RECOVERY_EXECUTABLES += recovery_flash_image +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := dump_image.c mtdutils.c mounts.c +LOCAL_MODULE := recovery_dump_image +LOCAL_MODULE_TAGS := eng +LOCAL_STATIC_LIBRARIES := libcutils libc +LOCAL_MODULE_STEM := dump_image +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +ADDITIONAL_RECOVERY_EXECUTABLES += recovery_dump_image +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := dump_image.c mtdutils.c mounts.c +LOCAL_MODULE := dump_image +LOCAL_MODULE_TAGS := eng include $(BUILD_EXECUTABLE) endif # TARGET_ARCH == arm diff --git a/dump_image/dump_image.c b/mtdutils/dump_image.c similarity index 100% rename from dump_image/dump_image.c rename to mtdutils/dump_image.c diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c index 18e6a5d..25feabd 100644 --- a/mtdutils/mtdutils.c +++ b/mtdutils/mtdutils.c @@ -38,6 +38,7 @@ struct MtdPartition { struct MtdReadContext { const MtdPartition *partition; char *buffer; + size_t read_size; size_t consumed; int fd; }; @@ -564,3 +565,84 @@ off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) { } return pos; } + + +ssize_t mtd_read_raw(MtdReadContext *ctx, char *data, size_t len) +{ + static const int SPARE_SIZE = (2048 >> 5); + struct mtd_info_user mtd_info; + struct mtd_oob_buf oob_buf; + struct nand_ecclayout ecc_layout; + struct nand_oobfree *fp; + unsigned char ecc[SPARE_SIZE]; + char *src, *dst; + int i, n, ret; + +/* + * FIXME: These two ioctls should be cached in MtdReadContext. + */ + ret = ioctl(ctx->fd, MEMGETINFO, &mtd_info); + if (ret < 0) + return -1; + + ret = ioctl(ctx->fd, ECCGETLAYOUT, &ecc_layout); + if (ret < 0) + return -1; + + ctx->read_size = mtd_info.writesize; + ctx->consumed = ctx->read_size; + +/* + * Read next good data block. + */ + ret = read_block(ctx->partition, ctx->fd, data); + if (ret < 0) + return -1; + + dst = src = data + ctx->read_size; + +/* + * Read OOB data for last block read in read_block(). + */ + oob_buf.start = lseek(ctx->fd, 0, SEEK_CUR) - ctx->read_size; + oob_buf.length = mtd_info.oobsize; + oob_buf.ptr = (unsigned char *) src; + + ret = ioctl(ctx->fd, MEMREADOOB, &oob_buf); + if (ret < 0) + return -1; + +/* + * As yaffs and yaffs2 use mode MEM_OOB_AUTO, but mtdchar uses + * MEM_OOB_PLACE, copy the spare data down the hard way. + * + * Safe away ECC data: + */ + for (i = 0; i < ecc_layout.eccbytes; i++) { + ecc[i] = src[ecc_layout.eccpos[i]]; + } + for ( ; i < SPARE_SIZE; i++) { + ecc[i] = 0; + } + +/* + * Copy yaffs2 spare data down. + */ + n = ecc_layout.oobavail; + fp = &ecc_layout.oobfree[0]; + while (n) { + if (fp->offset) { + memmove(dst, src + fp->offset, fp->length); + } + dst += fp->length; + n -= fp->length; + ++fp; + } + +/* + * Restore ECC data behind spare data. + */ + memcpy(dst, ecc, (ctx->read_size >> 5) - ecc_layout.oobavail); + + return ctx->read_size + (ctx->read_size >> 5); +} diff --git a/mtdutils/mtdutils.h b/mtdutils/mtdutils.h index 528a5bb..bd0ce17 100644 --- a/mtdutils/mtdutils.h +++ b/mtdutils/mtdutils.h @@ -52,4 +52,6 @@ off_t mtd_erase_blocks(MtdWriteContext *, int blocks); /* 0 ok, -1 for all */ off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos); int mtd_write_close(MtdWriteContext *); +ssize_t mtd_read_raw(MtdReadContext *ctx, char *data, size_t len); + #endif // MTDUTILS_H_ diff --git a/nandroid/Android.mk b/nandroid/Android.mk new file mode 100644 index 0000000..ae2e7fd --- /dev/null +++ b/nandroid/Android.mk @@ -0,0 +1,24 @@ +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_ARCH),arm) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := recovery_nandroid +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := nandroid-mobile.sh +LOCAL_MODULE_STEM := nandroid-mobile.sh +ADDITIONAL_RECOVERY_EXECUTABLES += recovery_nandroid +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := recovery_unyaffs +LOCAL_MODULE_STEM := unyaffs +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_SRC_FILES := unyaffs.c +LOCAL_STATIC_LIBRARIES := libc libcutils +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +include $(BUILD_EXECUTABLE) + +endif # TARGET_ARCH == arm +endif # !TARGET_SIMULATOR diff --git a/nandroid/nandroid-mobile.sh b/nandroid/nandroid-mobile.sh new file mode 100755 index 0000000..b5cae3d --- /dev/null +++ b/nandroid/nandroid-mobile.sh @@ -0,0 +1,1790 @@ +#!/sbin/sh + +# nandroid v2.2.2 - an Android backup tool for the G1 by infernix and brainaid +# restore capability added by cyanogen + +# pensive modified to allow to add prefixes to backups, and to restore specific backups +# pensive added the ability to exclude various images from the restore/backup operations, allows to preserve the newer +# recovery image if an older backup is being restored or to preserve user data. Also, saves space by not backing up +# partitions which change rarely. +# pensive added compressing backups and restoring compressed backups +# pensive added fetching system updates directly from the web into /sdcard/update.zip +# pensive added fetching system updates directly from the web into /cache and applying it. +# pensive added moving *update*.zip from /sdcard/download where a browser puts it to /sdcard/update.zip +# pensive added deletion of stale backups +# pensive added backup for ext2 partition on the sdcard to switch roms +# pensive added composite options --save NAME and --switchto NAME to switch ROMS +# pensive added list backup anywhere on the sdcard +# pensive added list updates (more precisely *.zip) anywhere on the sdcard +# Amon_RA : ext restore -> added check if ext backup is existing +# Amon_RA : ext restore -> added check if ext parition is existing + +# Requirements: + +# - a modded android in recovery mode (JF 1.3 will work by default) +# - adb shell as root in recovery mode if not using a pre-made recovery image +# - busybox in recovery mode +# - dump_image-arm-uclibc compiled and in path on phone +# - mkyaffs2image-arm-uclibc compiled and installed in path on phone +# - flash_image-arm-uclibc compiled and in path on phone +# - unyaffs-arm-uclibc compiled and in path on phone +# - for [de]compression needs gzip or bzip2, part of the busybox +# - wget for the wireless updates + +# Reference data: + +# dev: size erasesize name +#mtd0: 00040000 00020000 "misc" +#mtd1: 00500000 00020000 "recovery" +#mtd2: 00280000 00020000 "boot" +#mtd3: 04380000 00020000 "system" +#mtd4: 04380000 00020000 "cache" +#mtd5: 04ac0000 00020000 "userdata" +#mtd6 is everything, dump splash1 with: dd if=/dev/mtd/mtd6ro of=/sdcard/splash1.img skip=19072 bs=2048 count=150 + +# We don't dump misc or cache because they do not contain any useful data that we are aware of at this time. + +# Logical steps (v2.2.1): +# +# 0. test for a target dir and the various tools needed, if not found then exit with error. +# 1. check "adb devices" for a device in recovery mode. set DEVICEID variable to the device ID. abort when not found. +# 2. mount system and data partitions read-only, set up adb portforward and create destdir +# 3. check free space on /cache, exit if less blocks than 20MB free +# 4. push required tools to device in /cache +# 5 for partitions boot recovery misc: +# 5a get md5sum for content of current partition *on the device* (no data transfered) +# 5b while MD5sum comparison is incorrect (always is the first time): +# 5b1 dump current partition to a netcat session +# 5b2 start local netcat to dump image to current dir +# 5b3 compare md5sums of dumped data with dump in current dir. if correct, contine, else restart the loop (6b1) +# 6 for partitions system data: +# 6a get md5sum for tar of content of current partition *on the device* (no data transfered) +# 6b while MD5sum comparison is incorrect (always is the first time): +# 6b1 tar current partition to a netcat session +# 6b2 start local netcat to dump tar to current dir +# 6b3 compare md5sums of dumped data with dump in current dir. if correct, contine, else restart the loop (6b1) +# 6c if i'm running as root: +# 6c1 create a temp dir using either tempdir command or the deviceid in /tmp +# 6c2 extract tar to tempdir +# 6c3 invoke mkyaffs2image to create the img +# 6c4 clean up +# 7. remove tools from device /cache +# 8. umount system and data on device +# 9. print success. + + +DEVICEID=foo +RECOVERY=foo + +SUBNAME="" +NORECOVERY=0 +NOBOOT=0 +NODATA=0 +NOSYSTEM=0 +NOMISC=0 +NOCACHE=0 +NOSPLASH1=0 +NOSPLASH2=0 +EXT2=0 + +COMPRESS=0 +GETUPDATE=0 +RESTORE=0 +BACKUP=0 +DELETE=0 +WEBGET=0 +LISTBACKUP=0 +LISTUPDATE=0 +AUTOREBOOT=0 +AUTOAPPLY=0 +ITSANUPDATE=0 +ITSANIMAGE=0 +WEBGETSOURCE="" +WEBGETTARGET="/sdcard" + +DEFAULTUPDATEPATH="/sdcard/download" + +DEFAULTWEBUPDATE=http://n0rp.chemlab.org/android/update-cm-3.6.8.1-signed.zip +# There really should be a clone link always pointing to the latest +#DEFAULTWEBUPDATE="http://n0rp.chemlab.org/android/latestupdate-signed.zip" +DEFAULTWEBIMAGE=http://n0rp.chemlab.org/android/cm-recovery-1.4-signed.zip + +# Set up the default (for pensive at least) nameservers +NAMESERVER1=64.46.128.3 +NAMESERVER2=64.46.128.4 + +# WiFi works, rmnet0 setup ??? +# Do not know how to start the rmnet0 interface in recovery +# If in normal mode "ifconfig rmnet0 down" kills rild too +# /system/bin/rild& exits immediately, todo? + + +DEVICEID=`cat /proc/cmdline | sed "s/.*serialno=//" | cut -d" " -f1` + +# This is the default repository for backups +BACKUPPATH="/sdcard/nandroid/$DEVICEID" + + +# Boot, Cache, Data, Ext2, Misc, Recovery, System, Splash1, Splash2 +# BACKUPLEGEND, If all the partitions are backed up it should be "CBDEMRS12" +# Enables the user to figure at a glance what is in the backup +BACKUPLEGEND="" + +# Normally we want tar to be verbose for confidence building. +TARFLAGS="v" + +DEFAULTCOMPRESSOR=gzip +DEFAULTEXT=.gz +DEFAULTLEVEL="-1" + +ASSUMEDEFAULTUSERINPUT=0 + +echo2log() +{ + if [ -e /cache/recovery/log ]; then + echo $1 >>/cache/recovery/log + else + echo $1 >/cache/recovery/log + fi +} + +batteryAtLeast() +{ + REQUIREDLEVEL=$1 + ENERGY=`cat /sys/class/power_supply/battery/capacity` + if [ "`cat /sys/class/power_supply/battery/status`" == "Charging" ]; then + ENERGY=100 + fi + if [ ! $ENERGY -ge $REQUIREDLEVEL ]; then + $ECHO "Error: not enough battery power, need at least $REQUIREDLEVEL%." + $ECHO "Connect charger or USB power and try again" + exit 1 + fi +} + +if [ "`echo $0 | grep /sbin/nandroid-mobile.sh`" == "" ]; then + cp $0 /sbin + chmod 755 /sbin/`basename $0` + exec /sbin/`basename $0` $@ +fi + + +# Hm, have to handle old options for the current UI +case $1 in + restore) + shift + RESTORE=1 + ;; + backup) + shift + BACKUP=1 + ;; + --) + ;; +esac + +ECHO=echo +OUTPUT="" + +for option in $(getopt --name="nandroid-mobile v2.2.2" -l norecovery -l noboot -l nodata -l nosystem -l nocache -l nomisc -l nosplash1 -l nosplash2 -l subname: -l backup -l restore -l compress -l getupdate -l delete -l path -l webget: -l webgettarget: -l nameserver: -l nameserver2: -l bzip2: -l defaultinput -l autoreboot -l autoapplyupdate -l ext2 -l save: -l switchto: -l listbackup -l listupdate -l silent -l quiet -l help -- "cbruds:p:eql" "$@"); do + case $option in + --silent) + ECHO=echo2log + ASSUMEDEFAULTUSERINPUT=1 + TARFLAGS="" + OUTPUT=>>/cache/recovery/log + shift + ;; + --quiet) + ECHO=echo2log + ASSUMEDEFAULTUSERINPUT=1 + TARFLAGS="" + OUTPUT=>>/cache/recovery/log + shift + ;; + -q) + ECHO=echo2log + ASSUMEDEFAULTUSERINPUT=1 + TARFLAGS="" + OUTPUT=>>/cache/recovery/log + shift + ;; + --help) + ECHO=echo + $ECHO "Usage: $0 {--backup|--restore|--getupdate|--delete|--compress|--bzip2:ARG|--webget:URL|--listbackup|--listupdate} [options]" + $ECHO "" + $ECHO "--help Display this help" + $ECHO "" + $ECHO "-s | --subname: SUBSTRING In case of --backup the SUBSTRING is" + $ECHO " the prefix used with backup name" + $ECHO " in case of --restore or -c|--compress|--bzip2 or" + $ECHO " --delete SUBSTRING specifies backups on which to" + $ECHO " operate" + $ECHO "" + $ECHO "-u | --getupdate Will search /sdcard/download for files named" + $ECHO " *update*.zip, will prompt the user" + $ECHO " to narrow the choice if more than one is found," + $ECHO " and then move the latest, if not unique," + $ECHO " to sdcard root /sdcard with the update.zip name" + $ECHO " It is assumed the browser was used to put the *.zip" + $ECHO " in the /sdcard/download folder. -p|--path option" + $ECHO " would override /sdcard/download with the path of your" + $ECHO " choosing." + $ECHO "" + $ECHO "-p | --path DIR Requires an ARGUMENT, which is the path to where " + $ECHO " the backups are stored, can be used" + $ECHO " when the default path /sdcard/nandroid/$DEVICEID " + $ECHO " needs to be changed" + $ECHO "" + $ECHO "-b | --backup Will store a full system backup on $BACKUPPATH" + $ECHO " One can suppress backup of any image however with options" + $ECHO " starting with --no[partionname]" + $ECHO "" + $ECHO "-e | --ext2 Preserve the contents of the ext2 partition along with" + $ECHO " the other partitions being backed up, to easily switch roms." + $ECHO "" + $ECHO "-r | --restore Will restore the last made backup which matches --subname" + $ECHO " ARGUMENT for boot, system, recovery and data" + $ECHO " unless suppressed by other options" + $ECHO " if no --subname is supplied and the user fails to" + $ECHO " provide any input then the latest backup is used" + $ECHO " When restoring compressed backups, the images will remain" + $ECHO " decompressed after the restore, you need to use -c|-compress" + $ECHO " or --bzip2 to compress the backup again" + $ECHO "" + $ECHO "-d | --delete Will remove backups whose names match --subname ARGUMENT" + $ECHO " Will allow to narrow down, will ask if the user is certain." + $ECHO " Removes one backup at a time, repeat to remove multiple backups" + $ECHO "" + $ECHO "-c | --compress Will operate on chosen backups to compress image files," + $ECHO " resulting in saving of about 40MB out of 90+mb," + $ECHO " i.e. up to 20 backups can be stored in 1 GIG, if this " + $ECHO " option is turned on with --backup, the resulting backup will" + $ECHO " be compressed, no effect if restoring since restore will" + $ECHO " automatically uncompress compressed images if space is available" + $ECHO "" + $ECHO "--bzip2: -# Turns on -c|--compress and uses bzip2 for compression instead" + $ECHO " of gzip, requires an ARG -[1-9] compression level" + $ECHO "" + $ECHO "--webget: URL|\"\" Requires an argument, a valid URL for an *update*.zip file" + $ECHO " If a null string is passed then the default URL is used" + $ECHO " Will also create an update.MD5sum file and update.name with the" + $ECHO " original file name." + $ECHO "" + $ECHO "--nameserver: IP addr Provide the first nameserver IP address, to override the default" + $ECHO "" + $ECHO "--nameserver2: IP addr Provide the second nameserver IP address, to override the default" + $ECHO "" + $ECHO "--webgettarget: DIR Target directory to deposit the fetched update, default is" + $ECHO " /sdcard" + $ECHO "" + $ECHO "--norecovery Will suppress restore/backup of the recovery partition" + $ECHO " If recovery.img was not part of the backup, no need to use this" + $ECHO " option as the nandroid will not attempt to restore it, same goes" + $ECHO " for all the options below" + $ECHO "" + $ECHO "--noboot Will suppress restore/backup of the boot partition" + $ECHO "" + $ECHO "--nodata Will suppress restore/backup of the data partition" + $ECHO "" + $ECHO "--nosystem Will suppress restore/backup of the system partition" + $ECHO "" + $ECHO "--nocache Will suppress restore/backup of the cache partition" + $ECHO "" + $ECHO "--nomisc Will suppress restore/backup of the misc partition" + $ECHO "" + $ECHO "--nosplash1 Will suppress restore/backup of the splash1 partition" + $ECHO "" + $ECHO "--nosplash2 Will suppress restore/backup of the splash2 partition" + $ECHO "" + $ECHO "--defaultinput Makes nandroid-mobile non-interactive, assumes default" + $ECHO " inputs from the user." + $ECHO "" + $ECHO "--autoreboot Automatically reboot the phone after a backup, especially" + $ECHO " useful when the compression options are on -c|--compress| " + $ECHO " --bzip2 -level since the compression op takes time and" + $ECHO " you may want to go to sleep or do something else, and" + $ECHO " when a backup is over you want the calls and mms coming" + $ECHO " through, right?" + $ECHO "" + $ECHO "--autoapplyupdate Once the specific update is downloaded or chosen from the" + $ECHO " sdcard, apply it immediately. This option is valid as a " + $ECHO " modifier for either --webget or --getupdate options." + $ECHO "" + $ECHO "-e|--ext2 Save the contents of ext2 partition in the backup folder too." + $ECHO " Enables to keep different sets of apps for different ROMS and" + $ECHO " switch easily between them." + $ECHO "" + $ECHO "--save: ROMTAG Preserve EVERYTHING under ROMTAG name, a composite option," + $ECHO " it uses several other options." + $ECHO "" + $ECHO "--switchto: ROMTAG Restores your entire environment including app2sd apps, cache" + $ECHO " to the ROM environment named ROMTAG." + $ECHO "" + $ECHO "-q|--quiet|--silent Direct all the output to the recovery log, and assume default" + $ECHO " user inputs." + $ECHO "" + $ECHO "-l|--listbackup Will search the entire SDCARD for backup folders and will dump" + $ECHO " the list to stdout for use by the UI. Should be run with --silent" + $ECHO "" + $ECHO "--listupdate Will search the entire SDCARD for updates and will dump" + $ECHO " the list to stdout for use by the UI. Should be run with --silent" + exit 0 + ;; + --norecovery) + NORECOVERY=1 + #$ECHO "No recovery" + shift + ;; + --noboot) + NOBOOT=1 + #$ECHO "No boot" + shift + ;; + --nodata) + NODATA=1 + #$ECHO "No data" + shift + ;; + --nosystem) + NOSYSTEM=1 + #$ECHO "No system" + shift + ;; + --nocache) + NOCACHE=1 + #$ECHO "No cache" + shift + ;; + --nomisc) + NOMISC=1 + #$ECHO "No misc" + shift + ;; + --nosplash1) + NOSPLASH1=1 + #$ECHO "No splash1" + shift + ;; + --nosplash2) + NOSPLASH2=1 + #$ECHO "No splash2" + shift + ;; + --backup) + BACKUP=1 + #$ECHO "backup" + shift + ;; + -b) + BACKUP=1 + #$ECHO "backup" + shift + ;; + -e) + EXT2=1 + shift + ;; + --ext2) + EXT2=1 + shift + ;; + --restore) + RESTORE=1 + #$ECHO "restore" + shift + ;; + -r) + RESTORE=1 + #$ECHO "restore" + shift + ;; + --compress) + COMPRESS=1 + #$ECHO "Compress" + shift + ;; + -c) + COMPRESS=1 + #$ECHO "Compress" + shift + ;; + --bzip2) + COMPRESS=1 + DEFAULTCOMPRESSOR=bzip2 + DEFAULTEXT=.bz2 + if [ "$2" == "$option" ]; then + shift + fi + DEFAULTLEVEL="$2" + shift + ;; + --getupdate) + GETUPDATE=1 + shift + ;; + -u) + GETUPDATE=1 + shift + ;; + --subname) + if [ "$2" == "$option" ]; then + shift + fi + #$ECHO $2 + SUBNAME="$2" + shift + ;; + -s) + if [ "$2" == "$option" ]; then + shift + fi + #$ECHO $2 + SUBNAME="$2" + shift + ;; + --path) + if [ "$2" == "$option" ]; then + shift + fi + #$ECHO $2 + BACKUPPATH="$2" + DEFAULTUPDATEPATH="$2" + shift 2 + ;; + -p) + if [ "$2" == "$option" ]; then + shift + fi + #$ECHO $2 + BACKUPPATH="$2" + shift 2 + ;; + --delete) + DELETE=1 + shift + ;; + -d) + DELETE=1 + shift + ;; + --webgettarget) + if [ "$2" == "$option" ]; then + shift + fi + WEBGETTARGET="$2" + shift + ;; + --webget) + if [ "$2" == "$option" ]; then + shift + fi + #$ECHO "WEBGET" + # if the argument is "" stick with the default: /sdcard + if [ ! "$2" == "" ]; then + WEBGETSOURCE="$2" + fi + WEBGET=1 + shift + ;; + --nameserver) + if [ "$2" == "$option" ]; then + shift + fi + NAMESERVER1="$2" + shift + ;; + --nameserver2) + if [ "$2" == "$option" ]; then + shift + fi + NAMESERVER2="$2" + shift + ;; + --defaultinput) + ASSUMEDEFAULTUSERINPUT=1 + shift + ;; + --autoreboot) + AUTOREBOOT=1 + shift + ;; + --autoapplyupdate) + AUTOAPPLY=1 + shift + ;; + --save) + if [ "$2" == "$option" ]; then + shift + fi + SUBNAME="$2" + BACKUP=1 + ASSUMEDEFAULTUSERINPUT=1 + EXT2=1 + COMPRESS=1 + shift + ;; + --switchto) + if [ "$2" == "$option" ]; then + shift + fi + SUBNAME="$2" + RESTORE=1 + ASSUMEDEFAULTUSERINPUT=1 + EXT2=1 + shift + ;; + -l) + shift + LISTBACKUP=1 + ;; + --listbackup) + shift + LISTBACKUP=1 + ;; + --listupdate) + shift + LISTUPDATE=1 + ;; + --) + shift + break + ;; + esac +done + +$ECHO "" +$ECHO "nandroid-mobile v2.2.1" +$ECHO "" + +let OPS=$BACKUP +let OPS=$OPS+$RESTORE +let OPS=$OPS+$DELETE +let OPS=$OPS+$WEBGET +let OPS=$OPS+$GETUPDATE +let OPS=$OPS+$LISTBACKUP +let OPS=$OPS+$LISTUPDATE + +if [ "$OPS" -ge 2 ]; then + ECHO=echo + $ECHO "--backup, --restore, --delete, --webget, --getupdate, --listbackup, --listupdate are mutually exclusive operations." + $ECHO "Please, choose one and only one option!" + $ECHO "Aborting." + exit 1 +fi + +let OPS=$OPS+$COMPRESS + +if [ "$OPS" -lt 1 ]; then + ECHO=echo + $ECHO "Usage: $0 {-b|--backup|-r|--restore|-d|--delete|-u|--getupdate|--webget|-c|--compress|--bzip2 -level|-l|--listbackup|--listupdate} [options]" + $ECHO "At least one operation must be defined, try $0 --help for more information." + exit 0 +fi + + +if [ "$LISTBACKUP" == 1 ]; then + umount /sdcard 2>/dev/null + mount /sdcard 2>/dev/null + CHECK=`mount | grep /sdcard` + if [ "$CHECK" == "" ]; then + echo "Error: sdcard not mounted, aborting." + exit 1 + else + find /sdcard | grep nandroid.md5 | sed s/.gz//g | sed s/.bz2//g | sed s/nandroid.md5//g + umount /sdcard 2>/dev/null + exit 0 + fi +fi + +if [ "$LISTUPDATE" == 1 ]; then + umount /sdcard 2>/dev/null + mount /sdcard 2>/dev/null + CHECK=`mount | grep /sdcard` + if [ "$CHECK" == "" ]; then + echo "Error: sdcard not mounted, aborting." + exit 1 + else + find /sdcard | grep .zip + umount /sdcard 2>/dev/null + exit 0 + fi +fi + +# Make sure it exists +touch /cache/recovery/log + + +if [ "$AUTOAPPLY" == 1 -a "$WEBGET" == 0 -a "$GETUPDATE" == 0 ]; then + ECHO=echo + $ECHO "The --autoapplyupdate option is valid only in conjunction with --webget or --getupdate." + $ECHO "Aborting." + exit 1 +fi + +if [ "$COMPRESS" == 1 ]; then + $ECHO "Compressing with $DEFAULTCOMPRESSOR, level $DEFAULTLEVEL" +fi + +if [ "$WEBGET" == 1 ]; then + $ECHO "Fetching $WEBGETSOURCE to target folder: $WEBGETTARGET" +fi + +if [ ! "$SUBNAME" == "" ]; then + if [ "$BACKUP" == 1 ]; then + if [ "$COMPRESS" == 1 ]; then + $ECHO "Using $SUBNAME- prefix to create a compressed backup folder" + else + $ECHO "Using $SUBNAME- prefix to create a backup folder" + fi + else + if [ "$RESTORE" == 1 -o "$DELETE" == 1 -o "$COMPRESS" == 1 ]; then + $ECHO "Searching for backup directories, matching $SUBNAME, to delete or restore" + $ECHO "or compress" + $ECHO "" + fi + fi +else + if [ "$BACKUP" == 1 ]; then + $ECHO "Using G1 keyboard, enter a prefix substring and then " + $ECHO -n "or just to accept default: " + if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then + read SUBNAME + else + $ECHO "Accepting default." + fi + $ECHO "" + if [ "$COMPRESS" == 1 ]; then + $ECHO "Using $SUBNAME- prefix to create a compressed backup folder" + else + $ECHO "Using $SUBNAME- prefix to create a backup folder" + fi + $ECHO "" + else + if [ "$RESTORE" == 1 -o "$DELETE" == 1 -o "$COMPRESS" == 1 ]; then + $ECHO "Using G1 keyboard, enter a directory name substring and then " + $ECHO -n "to find matches or just to accept default: " + if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then + read SUBNAME + else + $ECHO "Accepting default." + fi + $ECHO "" + $ECHO "Using $SUBNAME string to search for matching backup directories" + $ECHO "" + fi + fi +fi + +if [ "$BACKUP" == 1 ]; then + mkyaffs2image=`which mkyaffs2image` + if [ "$mkyaffs2image" == "" ]; then + mkyaffs2image=`which mkyaffs2image-arm-uclibc` + if [ "$mkyaffs2image" == "" ]; then + $ECHO "error: mkyaffs2image or mkyaffs2image-arm-uclibc not found in path" + exit 1 + fi + fi + dump_image=`which dump_image` + if [ "$dump_image" == "" ]; then + dump_image=`which dump_image-arm-uclibc` + if [ "$dump_image" == "" ]; then + $ECHO "error: dump_image or dump_image-arm-uclibc not found in path" + exit 1 + fi + fi +fi + +if [ "$RESTORE" == 1 ]; then + flash_image=`which flash_image` + if [ "$flash_image" == "" ]; then + flash_image=`which flash_image-arm-uclibc` + if [ "$flash_image" == "" ]; then + $ECHO "error: flash_image or flash_image-arm-uclibc not found in path" + exit 1 + fi + fi + unyaffs=`which unyaffs` + if [ "$unyaffs" == "" ]; then + unyaffs=`which unyaffs-arm-uclibc` + if [ "$unyaffs" == "" ]; then + $ECHO "error: unyaffs or unyaffs-arm-uclibc not found in path" + exit 1 + fi + fi +fi +if [ "$COMPRESS" == 1 ]; then + compressor=`busybox | grep $DEFAULTCOMPRESSOR` + if [ "$compressor" == "" ]; then + $ECHO "Warning: busybox/$DEFAULTCOMPRESSOR is not found" + $ECHO "No compression operations will be performed" + COMPRESS=0 + else + $ECHO "Found $DEFAULTCOMPRESSOR, will compress the backup" + fi +fi + +# 1 +DEVICEID=`cat /proc/cmdline | sed "s/.*serialno=//" | cut -d" " -f1` +RECOVERY=`cat /proc/cmdline | grep "androidboot.mode=recovery"` +if [ "$RECOVERY" == "foo" ]; then + $ECHO "Error: Must be in recovery mode, aborting" + exit 1 +fi +if [ "$DEVICEID" == "foo" ]; then + $ECHO "Error: device id not found in /proc/cmdline, aborting" + exit 1 +fi +if [ ! "`id -u 2>/dev/null`" == "0" ]; then + if [ "`whoami 2>&1 | grep 'uid 0'`" == "" ]; then + $ECHO "Error: must run as root, aborting" + exit 1 + fi +fi + + +if [ "$RESTORE" == 1 ]; then + batteryAtLeast 30 +# ENERGY=`cat /sys/class/power_supply/battery/capacity` +# if [ "`cat /sys/class/power_supply/battery/status`" == "Charging" ]; then +# ENERGY=100 +# fi +# if [ ! $ENERGY -ge 30 ]; then +# $ECHO "Error: not enough battery power" +# $ECHO "Connect charger or USB power and try again" +# exit 1 +# fi + + + umount /sdcard 2>/dev/null + mount /sdcard 2>/dev/null + if [ "`mount | grep sdcard`" == "" ]; then + $ECHO "error: unable to mount /sdcard, aborting" + exit 1 + fi + + # find the latest backup, but show the user other options + $ECHO "" + $ECHO "Looking for the latest backup, will display other choices!" + $ECHO "" + + RESTOREPATH=`ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | tail -1` + $ECHO " " + + if [ "$RESTOREPATH" = "" ]; + then + $ECHO "Error: no backups found" + exit 2 + else + $ECHO "Default backup is the latest: $RESTOREPATH" + $ECHO "" + $ECHO "Other available backups are: " + $ECHO "" + ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | grep -v $RESTOREPATH $OUTPUT + $ECHO "" + $ECHO "Using G1 keyboard, enter a unique name substring to change it and " + $ECHO -n "or just to accept: " + if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then + read SUBSTRING + else + $ECHO "Accepting default." + SUBSTRING="" + fi + $ECHO "" + + if [ ! "$SUBSTRING" == "" ]; then + RESTOREPATH=`ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | grep $SUBSTRING | tail -1` + else + RESTOREPATH=`ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | tail -1` + fi + if [ "$RESTOREPATH" = "" ]; then + $ECHO "Error: no matching backups found, aborting" + exit 2 + fi + fi + + $ECHO "Restore path: $RESTOREPATH" + $ECHO "" + + umount /system /data 2>/dev/null + mount /system 2>/dev/null + mount /data 2>/dev/null + if [ "`mount | grep data`" == "" ]; then + $ECHO "error: unable to mount /data, aborting" + exit 1 + fi + if [ "`mount | grep system`" == "" ]; then + $ECHO "error: unable to mount /system, aborting" + exit 1 + fi + + CWD=$PWD + cd $RESTOREPATH + + DEFAULTEXT="" + if [ `ls *.bz2 2>/dev/null|wc -l` -ge 1 ]; then + DEFAULTCOMPRESSOR=bzip2 + DEFAULTDECOMPRESSOR=bunzip2 + DEFAULTEXT=.bz2 + fi + if [ `ls *.gz 2>/dev/null|wc -l` -ge 1 ]; then + DEFAULTCOMPRESSOR=gzip + DEFAULTDECOMPRESSOR=gunzip + DEFAULTEXT=.gz + fi + + if [ ! -f $RESTOREPATH/nandroid.md5$DEFAULTEXT ]; then + $ECHO "error: $RESTOREPATH/nandroid.md5 not found, cannot verify backup data" + exit 1 + fi + + if [ `ls *.bz2 2>/dev/null|wc -l` -ge 1 -o `ls *.gz 2>/dev/null|wc -l` -ge 1 ]; then + $ECHO "This backup is compressed with $DEFAULTCOMPRESSOR." + + # Make sure that $DEFAULT[DE]COMPRESSOR exists + if [ `busybox | grep $DEFAULTCOMPRESSOR | wc -l` -le 0 -a\ + `busybox | grep $DEFAULTDECOMPRESSOR | wc -l` -le 0 ]; then + + $ECHO "You do not have either the $DEFAULTCOMPRESSOR or the $DEFAULTDECOMPRESSOR" + $ECHO "to unpack this backup, cleaning up and aborting!" + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + exit 1 + fi + $ECHO "Checking free space /sdcard for the decompression operation." + FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" + # we need about 100MB for gzip to uncompress the files + if [ $FREEBLOCKS -le 100000 ]; then + $ECHO "Error: not enough free space available on sdcard (need about 100mb)" + $ECHO "to perform restore from the compressed images, aborting." + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + exit 1 + fi + $ECHO "Decompressing images, please wait...." + $ECHO "" + # Starting from the largest while we still have more space to reduce + # space requirements + $DEFAULTCOMPRESSOR -d `ls -S *$DEFAULTEXT` + $ECHO "Backup images decompressed" + $ECHO "" + fi + + $ECHO "Verifying backup images..." + md5sum -c nandroid.md5 + if [ $? -eq 1 ]; then + $ECHO "Error: md5sum mismatch, aborting" + exit 1 + fi + + if [ `ls boot* 2>/dev/null | wc -l` == 0 ]; then + NOBOOT=1 + fi + if [ `ls recovery* 2>/dev/null | wc -l` == 0 ]; then + NORECOVERY=1 + fi + if [ `ls data* 2>/dev/null | wc -l` == 0 ]; then + NODATA=1 + fi + if [ `ls system* 2>/dev/null | wc -l` == 0 ]; then + NOSYSTEM=1 + fi + # Amon_RA : If there's no ext backup set EXT2 to 0 so ext2 restore doesn't start + if [ `ls ext2* 2>/dev/null | wc -l` == 0 ]; then + EXT2=0 + fi + + for image in boot recovery; do + if [ "$NOBOOT" == "1" -a "$image" == "boot" ]; then + $ECHO "" + $ECHO "Not flashing boot image!" + $ECHO "" + continue + fi + if [ "$NORECOVERY" == "1" -a "$image" == "recovery" ]; then + $ECHO "" + $ECHO "Not flashing recovery image!" + $ECHO "" + continue + fi + $ECHO "Flashing $image..." + $flash_image $image $image.img $OUTPUT + done + + for image in data system; do + if [ "$NODATA" == "1" -a "$image" == "data" ]; then + $ECHO "" + $ECHO "Not restoring data image!" + $ECHO "" + continue + fi + if [ "$NOSYSTEM" == "1" -a "$image" == "system" ]; then + $ECHO "" + $ECHO "Not restoring system image!" + $ECHO "" + continue + fi + $ECHO "Erasing /$image..." + cd /$image + rm -rf * 2>/dev/null + $ECHO "Unpacking $image image..." + $unyaffs $RESTOREPATH/$image.img $OUTPUT + cd / + sync + umount /$image + done + + if [ "$EXT2" == 1 ]; then + # Amon_RA : Check if there's an ext partition before starting to restore + if [ -e /dev/block/mmcblk0p2 ]; then + $ECHO "Restoring the ext2 contents." + CWD=`pwd` + cd / + + if [ `mount | grep /system | wc -l` == 0 ]; then + mount /system + else + mount -o rw,remount /system + fi + + if [ `mount | grep /system/sd | wc -l` == 0 ]; then + mount /system/sd + fi + + cd $CWD + CHECK=`mount | grep /system/sd` + + if [ "$CHECK" == "" ]; then + $ECHO "Warning: --ext2 specified but unable to mount the ext2 partition." + $ECHO "Warning: your phone may be in an inconsistent state on reboot." + exit 1 + else + CWD=`pwd` + cd /system/sd + # Depending on whether the ext2 backup is compressed we do either or. + if [ -e $RESTOREPATH/ext2.tar ]; then + rm -rf * 2>/dev/null + tar -x$TARFLAGS -f $RESTOREPATH/ext2.tar + else + if [ -e $RESTOREPATH/ext2.tgz ]; then + rm -rf * 2>/dev/null + tar -x$TARFLAGS -zf $RESTOREPATH/ext2.tgz + else + if [ -e $RESTOREPATH/ext2.tar.bz2 ]; then + rm -rf * 2>/dev/null + tar -x$TARFLAGS -jf $RESTOREPATH/ext2.tar.bz2 + else + $ECHO "Warning: --ext2 specified but cannot find the ext2 backup." + $ECHO "Warning: your phone may be in an inconsistent state on reboot." + fi + fi + fi + cd $CWD + sync + umount /system/sd + umount /system + + fi + else + # Amon_RA : Just display a warning + $ECHO "Warning: --ext2 specified but ext2 partition present on sdcard" + $ECHO "Warning: your phone may be in an inconsistent state on reboot." + fi + fi + $ECHO "Restore done" + exit 0 +fi + +# 2. +if [ "$BACKUP" == 1 ]; then + + if [ "$COMPRESS" == 1 ]; then + ENERGY=`cat /sys/class/power_supply/battery/capacity` + if [ "`cat /sys/class/power_supply/battery/status`" == "Charging" ]; then + ENERGY=100 + fi + if [ ! $ENERGY -ge 30 ]; then + $ECHO "Warning: Not enough battery power to perform compression." + COMPRESS=0 + $ECHO "Turning off compression option, you can compress the backup later" + $ECHO "with the compression options." + fi + fi + +$ECHO "mounting system and data read-only, sdcard read-write" +umount /system 2>/dev/null +umount /data 2>/dev/null +umount /sdcard 2>/dev/null +mount -o ro /system || FAIL=1 +mount -o ro /data || FAIL=2 +mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=3 +case $FAIL in + 1) $ECHO "Error mounting system read-only"; umount /system /data /sdcard; exit 1;; + 2) $ECHO "Error mounting data read-only"; umount /system /data /sdcard; exit 1;; + 3) $ECHO "Error mounting sdcard read-write"; umount /system /data /sdcard; exit 1;; +esac + +if [ ! "$SUBNAME" == "" ]; then + SUBNAME=$SUBNAME- +fi + +# Identify the backup with what partitions have been backed up +if [ "$NOBOOT" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"B" +fi +if [ "$NOCACHE" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"C" +fi +if [ "$NODATA" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"D" +fi +if [ "$EXT2" == 1 ]; then + BACKUPLEGEND=$BACKUPLEGEND"E" +fi +if [ "$NOMISC" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"M" +fi +if [ "$NORECOVERY" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"R" +fi +if [ "$NOSYSTEM" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"S" +fi + +if [ ! -e /dev/mtd/mtd6ro ]; then + NOSPLASH1=1 + NOSPLASH2=1 +fi + +if [ "$NOSPLASH1" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"1" +fi +if [ "$NOSPLASH2" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"2" +fi + +if [ ! "$BACKUPLEGEND" == "" ]; then + BACKUPLEGEND=$BACKUPLEGEND- +fi + + +TIMESTAMP="`date +%Y%m%d-%H%M`" +DESTDIR="$BACKUPPATH/$SUBNAME$BACKUPLEGEND$TIMESTAMP" +if [ ! -d $DESTDIR ]; then + mkdir -p $DESTDIR + if [ ! -d $DESTDIR ]; then + $ECHO "error: cannot create $DESTDIR" + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + exit 1 + fi +else + touch $DESTDIR/.nandroidwritable + if [ ! -e $DESTDIR/.nandroidwritable ]; then + $ECHO "error: cannot write to $DESTDIR" + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + exit 1 + fi + rm $DESTDIR/.nandroidwritable +fi + +# 3. +$ECHO "checking free space on sdcard" +FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" +# we need about 130MB for the dump +if [ $FREEBLOCKS -le 130000 ]; then + $ECHO "Error: not enough free space available on sdcard (need 130mb), aborting." + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + exit 1 +fi + + + +if [ -e /dev/mtd/mtd6ro ]; then + if [ "$NOSPLASH1" == 0 ]; then + $ECHO -n "Dumping splash1 from device over tcp to $DESTDIR/splash1.img..." + dd if=/dev/mtd/mtd6ro of=$DESTDIR/splash1.img skip=19072 bs=2048 count=150 2>/dev/null + $ECHO "done" + sleep 1s + else + $ECHO "Dump of the splash1 image suppressed." + fi + if [ "$NOSPLASH2" == 0 ]; then + $ECHO -n "Dumping splash2 from device over tcp to $DESTDIR/splash2.img..." + dd if=/dev/mtd/mtd6ro of=$DESTDIR/splash2.img skip=19456 bs=2048 count=150 2>/dev/null + $ECHO "done" + else + $ECHO "Dump of the splash2 image suppressed." + fi +fi + + +# 5. +for image in boot recovery misc; do + + case $image in + boot) + if [ "$NOBOOT" == 1 ]; then + $ECHO "Dump of the boot partition suppressed." + continue + fi + ;; + recovery) + if [ "$NORECOVERY" == 1 ]; then + $ECHO "Dump of the recovery partition suppressed." + continue + fi + ;; + misc) + if [ "$NOMISC" == 1 ]; then + $ECHO "Dump of the misc partition suppressed." + continue + fi + ;; + esac + + # 5a + DEVICEMD5=`$dump_image $image - | md5sum | awk '{ print $1 }'` + sleep 1s + MD5RESULT=1 + # 5b + $ECHO -n "Dumping $image to $DESTDIR/$image.img..." + ATTEMPT=0 + while [ $MD5RESULT -eq 1 ]; do + let ATTEMPT=$ATTEMPT+1 + # 5b1 + $dump_image $image $DESTDIR/$image.img $OUTPUT + sync + # 5b3 + echo "${DEVICEMD5} $DESTDIR/$image.img" | md5sum -c -s - $OUTPUT + if [ $? -eq 1 ]; then + true + else + MD5RESULT=0 + fi + if [ "$ATTEMPT" == "5" ]; then + $ECHO "Fatal error while trying to dump $image, aborting." + umount /system + umount /data + umount /sdcard + exit 1 + fi + done + $ECHO "done" +done + +# 6 +for image in system data cache; do + case $image in + system) + if [ "$NOSYSTEM" == 1 ]; then + $ECHO "Dump of the system partition suppressed." + continue + fi + ;; + data) + if [ "$NODATA" == 1 ]; then + $ECHO "Dump of the data partition suppressed." + continue + fi + ;; + cache) + if [ "$NOCACHE" == 1 ]; then + $ECHO "Dump of the cache partition suppressed." + continue + fi + ;; + esac + + # 6a + $ECHO -n "Dumping $image to $DESTDIR/$image.img..." + $mkyaffs2image /$image $DESTDIR/$image.img $OUTPUT + sync + $ECHO "done" +done + +# Backing up the ext2 partition, not really for the backup but to switch ROMS and apps at the same time. + +if [ "$EXT2" == 1 ]; then + $ECHO "Storing the ext2(Apps, Dalvik-cache) contents in the backup folder." + + CHECK1=`mount | grep /system` + if [ "$CHECK1" == "" ]; then + mount /system 2>/dev/null + fi + CHECK2=`mount | grep /system/sd` + if [ "$CHECK2" == "" ]; then + mount /system/sd 2>/dev/null + fi + + CHECK1=`mount | grep /system` + CHECK2=`mount | grep /system/sd` + if [ "$CHECK1" == "" -o "$CHECK2" == "" ]; then + $ECHO "Warning: --ext2 specified but unable to mount the ext2 partition." + exit 1 + else + + CWD=`pwd` + cd /system/sd + # Depending on the whether we want it compressed we do either or. + if [ "$COMPRESS" == 0 ]; then + tar -cvf $DESTDIR/ext2.tar ./ + else + if [ "$DEFAULTCOMPRESSOR" == "bzip2" ]; then + tar -cvjf $DESTDIR/ext2.tar.bz2 ./ + else + tar -cvzf $DESTDIR/ext2.tgz ./ + fi + fi + cd $CWD + umount /system/sd + fi +fi + + +# 7. +$ECHO -n "generating md5sum file..." +CWD=$PWD +cd $DESTDIR +md5sum *img > nandroid.md5 + +# 7b. +if [ "$COMPRESS" == 1 ]; then + $ECHO "Compressing the backup, may take a bit of time, please wait..." + $ECHO "checking free space on sdcard for the compression operation." + FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" + # we need about 70MB for the intermediate storage needs + if [ $FREEBLOCKS -le 70000 ]; then + $ECHO "error: not enough free space available on sdcard for compression operation (need 70mb)" + $ECHO "leaving this backup uncompressed." + else + # we are already in $DESTDIR, start compression from the smallest files + # to maximize space for the largest's compression, less likely to fail. + # To decompress reverse the order. + $DEFAULTCOMPRESSOR $DEFAULTLEVEL `ls -S -r * | grep -v ext2` + fi +fi + +cd $CWD +$ECHO "done" + +# 8. +$ECHO "unmounting system, data and sdcard" +umount /system +umount /data +umount /sdcard + +# 9. +$ECHO "Backup successful." +if [ "$AUTOREBOOT" == 1 ]; then + reboot +fi +exit 0 +fi + + +# ----------------------------------GETTING UPDATES DIRECT FROM THE WEB USING WIFI------------- + +if [ "$WEBGET" == 1 ]; then + $ECHO "mounting system and data read-only, sdcard read-write" + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + + # Since we are in recovery, these file-systems have to be mounted + $ECHO "Mounting /system and /data for starting WiFi" + mount -o ro /system || FAIL=1 + # Need to write to this system to setup nameservers for the wifi + mount -o rw /data || FAIL=2 + mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=3 + + case $FAIL in + 1) $ECHO "Error mounting system read-only"; umount /system /data /sdcard; exit 1;; + 2) $ECHO "Error mounting data read-write"; umount /system /data /sdcard; exit 1;; + 3) $ECHO "Error mounting sdcard read-write"; umount /system /data /sdcard; exit 1;; + esac + + if [ "$WEBGETSOURCE" == "" ]; then + # Set the URL to the current latest update + if [ "$ITSANUPDATE" == 1 ]; then + WEBGETSOURCE=$DEFAULTWEBUPDATE + else + WEBGETSOURCE=$DEFAULTWEBIMAGE + fi + fi + + if [ "$AUTOAPPLY" == 0 ]; then + # Need to check space on sdcard only if we dump the update there. + $ECHO "Checking free space on sdcard for the update download." + FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" + # we need about 50MB for the storage needs + if [ $FREEBLOCKS -le 50000 ]; then + $ECHO "Error: not enough free space available on sdcard for the update operation (need 50mb)" + $ECHO "Please free up space before invoking this option again." + $ECHO "Cleaning up, unmounting file systems, aborting." + umount /system /data /sdcard + exit 1 + fi + fi + + if [ ! `basename $WEBGETSOURCE` == `basename $WEBGETSOURCE .zip` ]; then + # It is a zip, not img. + ITSANUPDATE=1 + else + if [ ! `basename $WEBGETSOURCE` == `basename $WEBGETSOURCE .img` ]; then + # It is an img file. + ITSANIMAGE=1 + else + # Unknown file type + $ECHO "Unknown file type, cleaning up, aborting." + umount /system /data /sdcard + exit 1 + fi + fi + + + if [ "$ITSANUPDATE" == 1 -a "$AUTOAPPLY" == 0 ]; then + # Move the previous update aside, if things go badly with the new update, it is good + # to have the last one still around :-) + + # If we cannot figure out what the file name used to be, create this new one with a time stamp + OLDNAME="OLD-update-`date +%Y%m%d-%H%M`" + + if [ -e $WEBGETTARGET/update.zip ]; then + $ECHO "There is already an update.zip in $WEBGETTARGET, backing it up to" + if [ -e $WEBGETTARGET/update.name ]; then + OLDNAME=`cat $WEBGETTARGET/update.name` + # Backup the name file (presumably contains the old name of the update.zip + mv -f $WEBGETTARGET/update.name $WEBGETTARGET/`basename $OLDNAME .zip`.name + fi + $ECHO "`basename $OLDNAME .zip`.zip" + mv -f $WEBGETTARGET/update.zip $WEBGETTARGET/`basename $OLDNAME .zip`.zip + + # Backup the MD5sum file + if [ -e $WEBGETTARGET/update.MD5sum ]; then + mv -f $WEBGETTARGET/update.MD5sum $WEBGETTARGET/`basename $OLDNAME .zip`.MD5sum + fi + fi + fi + + $ECHO "Starting WiFI, please wait..." + insmod /system/lib/modules/wlan.ko + + wlan_loader -f /system/etc/wifi/Fw1251r1c.bin -e /proc/calibration -i /system/etc/wifi/tiwlan.ini + + CWD=`pwd` + cd /data/local/tmp + + wpa_supplicant -f -Dtiwlan0 -itiwlan0 -c/data/misc/wifi/wpa_supplicant.conf& + + sleep 5 + $ECHO "wpa_supplicant started" + $ECHO "" + + echo "nameserver $NAMESERVER1" >/etc/resolv.conf + echo "nameserver $NAMESERVER2" >>/etc/resolv.conf + + #We want the wifi to assign a dynamic address + $ECHO "Starting DHCPCD server (dynamic address assignment)" + # -BKL flags???? + dhcpcd -d tiwlan0 2>/dev/null & + + # Have to wait for it to init stuff + sleep 10 + + + CHECK1=`ps | grep -v grep | grep dhcpcd` + CHECK2=`ps | grep -v grep | grep wpa_supplicant` + if [ "$CHECK1" == "" -o "$CHECK2" == "" ]; then + $ECHO "Error: wpa_supplicant or DHCPCD server is not running, cleaning up, aborting" + rm -- -Dtiwlan0 + cd $CWD + + $ECHO "unmounting /system, /data and /sdcard" + umount /system + umount /data + umount /sdcard + exit 2 + fi + + $ECHO "DHCPCD server started" + $ECHO "" + + $ECHO "WiFi is running!" + $ECHO "" + + if [ "$AUTOAPPLY" == 1 ]; then + $ECHO "Autoapply is on, retrieving the update into /cache/`basename $WEBGETSOURCE`" + + wget -O /cache/`basename $WEBGETSOURCE` $WEBGETSOURCE $OUTPUT + + if [ ! -e /cache/recovery ]; then + mkdir /cache/recovery + chmod 777 /cache/recovery + fi + if [ -e /cache/recovery/command ]; then + echo "--update_package=CACHE:`basename $WEBGETSOURCE`" >>/cache/recovery/command + else + echo "--update_package=CACHE:`basename $WEBGETSOURCE`" >/cache/recovery/command + fi + chmod 555 /cache/recovery/command + # Once rebooted the update will be applied. + + else + + if [ "$ITSANUPDATE" == 1 ]; then + $ECHO "Retrieving system update into $WEBGETTARGET/update.zip, please wait..." + wget -O $WEBGETTARGET/update.zip $WEBGETSOURCE $OUTPUT + + echo "`basename $WEBGETSOURCE`" > $WEBGETTARGET/update.name + $ECHO "" + $ECHO "Update retrieved, if concerned, please compare the md5sum with the number" + $ECHO "you see on the web page, if it is NOT the same, the retrieval" + $ECHO "has failed and has to be repeated." + $ECHO "" + $ECHO `md5sum $WEBGETTARGET/update.zip | tee $WEBGETTARGET/update.MD5sum` + $ECHO "" + $ECHO "MD5sum has been stored in $WEBGETTARGET/update.MD5sum" + else + $ECHO "Retrieving the image into $WEBGETTARGET/`basename $WEBGETSOURCE`, please wait..." + wget -O $WEBGETTARGET/`basename $WEBGETSOURCE` $WEBGETSOURCE $OUTPUT + $ECHO "" + $ECHO "$WEBGETSOURCE retrieved, if concerned, please compare the md5sum with the number" + $ECHO "you see on the web page, if it is NOT the same, the retrieval" + $ECHO "has failed and has to be repeated." + $ECHO "" + md5sum $WEBGETTARGET/`basename $WEBGETSOURCE` | tee $WEBGETTARGET/`basename $WEBGETSOURCE .img`.MD5sum $OUTPUT + $ECHO "" + $ECHO "MD5sum has been stored in $WEBGETTARGET/`basename $WEBGETSOURCE .img`.MD5sum" + $ECHO "" + $ECHO -n "Would you like to flash this image into boot or recovery? (or no for no flash) " + read ANSWER + if [ "$ANSWER" == "boot" ]; then + $ECHO "Flashing $WEBGETTARGET/`basename $WEBGETSOURCE` into the boot partition." + $flash_image boot $WEBGETTARGET/`basename $WEBGETSOURCE` + else + if [ "$ANSWER" == "recovery" ]; then + $ECHO "Moving $WEBGETTARGET/`basename $WEBGETSOURCE` into the /data/recovery.img" + $ECHO "and /system/recovery.img" + cp -f $WEBGETTARGET/`basename $WEBGETSOURCE` /data/recovery.img + mount -o rw,remount /system + cp -f $WEBGETTARGET/`basename $WEBGETSOURCE` /system/recovery.img + $ECHO "Depending on the settings of your specific ROM, the recovery.img will be" + $ECHO "flashed at the normal bootup time either from /system or /data." + else + $ECHO "Not flashing the image." + fi + fi + fi + $ECHO "" + + fi + + $ECHO "Shutting down DHCPCD service and wpa_supplicant" + killall -TERM dhcpcd + TEMPVAR=`ps | grep -v grep | grep wpa_supplicant` + TEMPVAR=`echo $TEMPVAR | cut -f 1 -d ' '` + kill -TERM $TEMPVAR + + while true; do + CHECK=`ps | grep -v grep | grep dhcpcd` + if [ ! "$CHECK" == "" ]; then + sleep 1 + else + break + fi + done + + while true; do + CHECK=`ps | grep -v grep | grep wpa_supplicant` + if [ ! "$CHECK" == "" ]; then + sleep 1 + else + break + fi + done + #sleep 5 + + $ECHO "Cleaning up..." + # Looks like cannot clean up wlan module since chdir is missing + #rmmod wlan + rm -- -Dtiwlan0 + cd $CWD + + $ECHO "unmounting /system, /data and /sdcard" + umount /system + umount /data + umount /sdcard + + if [ "$AUTOAPPLY" == 1 ]; then + $ECHO "Auto apply update is on, rebooting into recovery to apply the update." + $ECHO "When the update is complete reboot into the normal mode." + $ECHO "The device will reboot and the update will be applied in 10 seconds!" + sleep 10 + reboot recovery + else + if [ "$ITSANUPDATE" == 1 ]; then + $ECHO "If you put the update into a folder other than /sdcard you need to use --getupdate to" + $ECHO "prepare the update for application." + $ECHO "You may want to execute 'reboot recovery' and choose update option to flash the update." + $ECHO "Or in the alternative, shutdown your phone with reboot -p, and then press +" + $ECHO "to initiate a normal system update procedure, if you have stock SPL." + fi + exit 0 + fi +fi + +# -------------------------------------DELETION, COMPRESSION OF BACKUPS--------------------------------- +if [ "$COMPRESS" == 1 -o "$DELETE" == 1 ]; then + $ECHO "Unmounting /system and /data to be on the safe side, mounting /sdcard read-write." + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + + FAIL=0 + # Since we are in recovery, these file-system have to be mounted + $ECHO "Mounting /sdcard to look for backups." + mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=1 + + if [ "$FAIL" == 1 ]; then + $ECHO "Error mounting /sdcard read-write, cleaning up..."; umount /system /data /sdcard; exit 1 + fi + + $ECHO "The current size of /sdcard FAT32 filesystem is `du /sdcard | tail -1 | cut -f 1 -d '/'`Kb" + $ECHO "" + + # find the oldest backup, but show the user other options + $ECHO "Looking for the oldest backup to delete, newest to compress," + $ECHO "will display all choices!" + $ECHO "" + $ECHO "Here are the backups you have picked within this repository $BACKUPPATH:" + + if [ "$DELETE" == 1 ]; then + RESTOREPATH=`ls -td $BACKUPPATH/*$SUBNAME* 2>/dev/null | tail -1` + ls -td $BACKUPPATH/*$SUBNAME* 2>/dev/null $OUTPUT + else + RESTOREPATH=`ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | tail -1` + ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null $OUTPUT + fi + $ECHO " " + + if [ "$RESTOREPATH" = "" ]; then + $ECHO "Error: no backups found" + exit 2 + else + if [ "$DELETE" == 1 ]; then + $ECHO "Default backup to delete is the oldest: $RESTOREPATH" + $ECHO "" + $ECHO "Other candidates for deletion are: " + ls -td $BACKUPPATH/*$SUBNAME* 2>/dev/null | grep -v $RESTOREPATH $OUTPUT + fi + if [ "$COMPRESS" == 1 ]; then + $ECHO "Default backup to compress is the latest: $RESTOREPATH" + $ECHO "" + $ECHO "Other candidates for compression are: " + ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | grep -v $RESTOREPATH $OUTPUT + fi + + $ECHO "" + $ECHO "Using G1 keyboard, enter a unique name substring to change it and " + $ECHO -n "or just to accept: " + if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then + read SUBSTRING + else + $ECHO "Accepting default." + SUBSTRING="" + fi + + if [ ! "$SUBSTRING" == "" ]; then + RESTOREPATH=`ls -td $BACKUPPATH/*$SUBNAME* 2>/dev/null | grep $SUBSTRING | tail -1` + else + RESTOREPATH=`ls -td $BACKUPPATH/*$SUBNAME* 2>/dev/null | tail -1` + fi + if [ "$RESTOREPATH" = "" ]; then + $ECHO "Error: no matching backup found, aborting" + exit 2 + fi + fi + + if [ "$DELETE" == 1 ]; then + $ECHO "Deletion path: $RESTOREPATH" + $ECHO "" + $ECHO "WARNING: Deletion of a backup is an IRREVERSIBLE action!!!" + $ECHO -n "Are you absolutely sure? {yes | YES | Yes | no | NO | No}: " + if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then + read ANSWER + else + ANSWER=yes + $ECHO "Accepting default." + fi + $ECHO "" + if [ "$ANSWER" == "yes" -o "$ANSWER" == "YES" -o "$ANSWER" == "Yes" ]; then + rm -rf $RESTOREPATH + $ECHO "" + $ECHO "$RESTOREPATH has been permanently removed from your SDCARD." + $ECHO "Post deletion size of the /sdcard FAT32 filesystem is `du /sdcard | tail -1 | cut -f 1 -d '/'`Kb" + else + if [ "$ANSWER" == "no" -o "$ANSWER" == "NO" -o "$ANSWER" == "No" ]; then + $ECHO "The chosen backup will NOT be removed." + else + $ECHO "Invalid answer: assuming NO." + fi + fi + fi + + if [ "$COMPRESS" == 1 ]; then + + CWD=`pwd` + cd $RESTOREPATH + + if [ `ls *.bz2 2>/dev/null|wc -l` -ge 1 -o `ls *.gz 2>/dev/null|wc -l` -ge 1 ]; then + $ECHO "This backup is already compressed, cleaning up, aborting..." + cd $CWD + umount /sdcard 2>/dev/null + exit 0 + fi + + $ECHO "checking free space on sdcard for the compression operation." + FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" + # we need about 70MB for the intermediate storage needs + if [ $FREEBLOCKS -le 70000 ]; then + $ECHO "Error: not enough free space available on sdcard for compression operation (need 70mb)" + $ECHO "leaving this backup uncompressed." + else + # we are already in $DESTDIR, start compression from the smallest files + # to maximize space for the largest's compression, less likely to fail. + # To decompress reverse the order. + $ECHO "Pre compression size of the /sdcard FAT32 filesystem is `du /sdcard | tail -1 | cut -f 1 -d '/'`Kb" + $ECHO "" + $ECHO "Compressing the backup may take a bit of time, please wait..." + $DEFAULTCOMPRESSOR $DEFAULTLEVEL `ls -S -r *` + $ECHO "" + $ECHO "Post compression size of the /sdcard FAT32 filesystem is `du /sdcard | tail -1 | cut -f 1 -d '/'`Kb" + fi + fi + + $ECHO "Cleaning up." + cd $CWD + umount /sdcard 2>/dev/null + exit 0 + +fi + +if [ "$GETUPDATE" == 1 ]; then + $ECHO "Unmounting /system and /data to be on the safe side, mounting /sdcard read-write." + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + + FAIL=0 + # Since we are in recovery, these file-system have to be mounted + $ECHO "Mounting /sdcard to look for updates to flash." + mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=1 + + if [ "$FAIL" == 1 ]; then + $ECHO "Error mounting /sdcard read-write, cleaning up..."; umount /system /data /sdcard; exit 1 + fi + + $ECHO "The current size of /sdcard FAT32 filesystem is `du /sdcard | tail -1 | cut -f 1 -d '/'`Kb" + $ECHO "" + + # find all the files with update in them, but show the user other options + $ECHO "Looking for all *update*.zip candidate files to flash." + $ECHO "" + $ECHO "Here are the updates limited by the subname $SUBNAME found" + $ECHO "within the repository $DEFAULTUPDATEPATH:" + $ECHO "" + RESTOREPATH=`ls -trd $DEFAULTUPDATEPATH/*$SUBNAME*.zip 2>/dev/null | grep update | tail -1` + if [ "$RESTOREPATH" == "" ]; then + $ECHO "Error: found no matching updates, cleaning up, aborting..." + umount /sdcard 2>/dev/null + exit 2 + fi + ls -trd $DEFAULTUPDATEPATH/*$SUBNAME*.zip 2>/dev/null | grep update $OUTPUT + $ECHO "" + $ECHO "The default update is the latest $RESTOREPATH" + $ECHO "" + $ECHO "Using G1 keyboard, enter a unique name substring to change it and " + $ECHO -n "or just to accept: " + if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then + read SUBSTRING + else + $ECHO "Accepting default." + SUBSTRING="" + fi + $ECHO "" + + if [ ! "$SUBSTRING" == "" ]; then + RESTOREPATH=`ls -trd $DEFAULTUPDATEPATH/*$SUBNAME*.zip 2>/dev/null | grep update | grep $SUBSTRING | tail -1` + else + RESTOREPATH=`ls -trd $DEFAULTUPDATEPATH/*$SUBNAME*.zip 2>/dev/null | grep update | tail -1` + fi + if [ "$RESTOREPATH" = "" ]; then + $ECHO "Error: no matching backups found, aborting" + exit 2 + fi + + if [ "$RESTOREPATH" == "/sdcard/update.zip" ]; then + $ECHO "You chose update.zip, it is ready for flashing, there nothing to do." + else + + # Things seem ok so far. + + # Move the previous update aside, if things go badly with the new update, it is good + # have the last one still around :-) + + # If we cannot figure out what the file name used to be, create this new one with a time stamp + OLDNAME="OLD-update-`date +%Y%m%d-%H%M`" + + if [ -e /sdcard/update.zip ]; then + $ECHO "There is already an update.zip in /sdcard, backing it up to" + if [ -e /sdcard/update.name ]; then + OLDNAME=`cat /sdcard/update.name` + # Backup the name file (presumably contains the old name of the update.zip + mv -f /sdcard/update.name /sdcard/`basename $OLDNAME .zip`.name + fi + $ECHO "`basename $OLDNAME .zip`.zip" + mv -f /sdcard/update.zip /sdcard/`basename $OLDNAME .zip`.zip + + # Backup the MD5sum file + if [ -e /sdcard/update.MD5sum ]; then + mv -f /sdcard/update.MD5sum /sdcard/`basename $OLDNAME .zip`.MD5sum + fi + fi + + if [ -e $DEFAULTUPDATEPATH/`basename $RESTOREPATH .zip`.MD5sum ]; then + mv -f $DEFAULTUPDATEPATH/`basename $RESTOREPATH .zip`.MD5sum /sdcard/update.MD5sum + else + $ECHO `md5sum $RESTOREPATH | tee /sdcard/update.MD5sum` + $ECHO "" + $ECHO "MD5sum has been stored in /sdcard/update.MD5sum" + $ECHO "" + fi + if [ -e $DEFAULTUPDATEPATH/`basename $RESTOREPATH .zip`.name ]; then + mv -f $DEFAULTUPDATEPATH/`basename $RESTOREPATH .zip`.name /sdcard/update.name + else + echo "`basename $RESTOREPATH`" > /sdcard/update.name + fi + + mv -i $RESTOREPATH /sdcard/update.zip + + + $ECHO "Your file $RESTOREPATH has been moved to the root of sdcard, and is ready for flashing!!!" + + fi + + $ECHO "You may want to execute 'reboot recovery' and then choose the update option to flash the update." + $ECHO "Or in the alternative, shutdown your phone with reboot -p, and then press + to" + $ECHO "initiate a standard update procedure if you have stock SPL." + $ECHO "" + $ECHO "Cleaning up and exiting." + umount /sdcard 2>/dev/null + exit 0 +fi diff --git a/nandroid/unyaffs.c b/nandroid/unyaffs.c new file mode 100644 index 0000000..aa8a735 --- /dev/null +++ b/nandroid/unyaffs.c @@ -0,0 +1,118 @@ +/* + * unyaffs: extract files from yaffs2 file system image to current directory + * + * Created by Kai Wei + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "unyaffs.h" + +#define CHUNK_SIZE 2048 +#define SPARE_SIZE 64 +#define MAX_OBJECTS 10000 +#define YAFFS_OBJECTID_ROOT 1 + + +unsigned char data[CHUNK_SIZE + SPARE_SIZE]; +unsigned char *chunk_data = data; +unsigned char *spare_data = data + CHUNK_SIZE; +int img_file; + +char *obj_list[MAX_OBJECTS]; +void process_chunk() +{ + int out_file, remain, s; + char *full_path_name; + + yaffs_PackedTags2 *pt = (yaffs_PackedTags2 *)spare_data; + if (pt->t.byteCount == 0xffff) { //a new object + + yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)malloc(sizeof(yaffs_ObjectHeader)); + memcpy(oh, chunk_data, sizeof(yaffs_ObjectHeader)); + + full_path_name = (char *)malloc(strlen(oh->name) + strlen(obj_list[oh->parentObjectId]) + 2); + if (full_path_name == NULL) { + perror("malloc full path name\n"); + } + strcpy(full_path_name, obj_list[oh->parentObjectId]); + strcat(full_path_name, "/"); + strcat(full_path_name, oh->name); + obj_list[pt->t.objectId] = full_path_name; + + switch(oh->type) { + case YAFFS_OBJECT_TYPE_FILE: + remain = oh->fileSize; + out_file = creat(full_path_name, oh->yst_mode); + while(remain > 0) { + if (read_chunk()) + return -1; + s = (remain < pt->t.byteCount) ? remain : pt->t.byteCount; + if (write(out_file, chunk_data, s) == -1) + return -1; + remain -= s; + } + close(out_file); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + symlink(oh->alias, full_path_name); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + mkdir(full_path_name, 0777); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + link(obj_list[oh->equivalentObjectId], full_path_name); + break; + } + } +} + + +int read_chunk() +{ + ssize_t s; + int ret = -1; + memset(chunk_data, 0xff, sizeof(chunk_data)); + s = read(img_file, data, CHUNK_SIZE + SPARE_SIZE); + if (s == -1) { + perror("read image file\n"); + } else if (s == 0) { + printf("end of image\n"); + } else if ((s == (CHUNK_SIZE + SPARE_SIZE))) { + ret = 0; + } else { + fprintf(stderr, "broken image file\n"); + } + return ret; +} + +int main(int argc, char **argv) +{ + if (argc != 2) { + printf("Usage: unyaffs image_file_name\n"); + exit(1); + } + img_file = open(argv[1], O_RDONLY); + if (img_file == -1) { + printf("open image file failed\n"); + exit(1); + } + + obj_list[YAFFS_OBJECTID_ROOT] = "."; + while(1) { + if (read_chunk() == -1) + break; + process_chunk(); + } + close(img_file); + return 0; +} diff --git a/nandroid/unyaffs.h b/nandroid/unyaffs.h new file mode 100644 index 0000000..fcd8046 --- /dev/null +++ b/nandroid/unyaffs.h @@ -0,0 +1,144 @@ +/* + * definition copied from yaffs project + */ + +#ifndef __UNYAFFS_H__ +#define __UNYAFFS_H__ + + +#define YAFFS_MAX_NAME_LENGTH 255 +#define YAFFS_MAX_ALIAS_LENGTH 159 + +#include + +/* Definition of types */ +#ifndef __ASM_ARM_TYPES_H +typedef unsigned char __u8; +typedef unsigned short __u16; +typedef unsigned __u32; +#endif +typedef struct { + unsigned sequenceNumber; + unsigned objectId; + unsigned chunkId; + unsigned byteCount; +} yaffs_PackedTags2TagsPart; + +typedef struct { + unsigned char colParity; + unsigned lineParity; + unsigned lineParityPrime; +} yaffs_ECCOther; + +typedef struct { + yaffs_PackedTags2TagsPart t; + yaffs_ECCOther ecc; +} yaffs_PackedTags2; + +typedef enum { + YAFFS_ECC_RESULT_UNKNOWN, + YAFFS_ECC_RESULT_NO_ERROR, + YAFFS_ECC_RESULT_FIXED, + YAFFS_ECC_RESULT_UNFIXED +} yaffs_ECCResult; + +typedef enum { + YAFFS_OBJECT_TYPE_UNKNOWN, + YAFFS_OBJECT_TYPE_FILE, + YAFFS_OBJECT_TYPE_SYMLINK, + YAFFS_OBJECT_TYPE_DIRECTORY, + YAFFS_OBJECT_TYPE_HARDLINK, + YAFFS_OBJECT_TYPE_SPECIAL +} yaffs_ObjectType; + + +typedef struct { + + unsigned validMarker0; + unsigned chunkUsed; /* Status of the chunk: used or unused */ + unsigned objectId; /* If 0 then this is not part of an object (unused) */ + unsigned chunkId; /* If 0 then this is a header, else a data chunk */ + unsigned byteCount; /* Only valid for data chunks */ + + /* The following stuff only has meaning when we read */ + yaffs_ECCResult eccResult; + unsigned blockBad; + + /* YAFFS 1 stuff */ + unsigned chunkDeleted; /* The chunk is marked deleted */ + unsigned serialNumber; /* Yaffs1 2-bit serial number */ + + /* YAFFS2 stuff */ + unsigned sequenceNumber; /* The sequence number of this block */ + + /* Extra info if this is an object header (YAFFS2 only) */ + + unsigned extraHeaderInfoAvailable; /* There is extra info available if this is not zero */ + unsigned extraParentObjectId; /* The parent object */ + unsigned extraIsShrinkHeader; /* Is it a shrink header? */ + unsigned extraShadows; /* Does this shadow another object? */ + + yaffs_ObjectType extraObjectType; /* What object type? */ + + unsigned extraFileLength; /* Length if it is a file */ + unsigned extraEquivalentObjectId; /* Equivalent object Id if it is a hard link */ + + unsigned validMarker1; + +} yaffs_ExtendedTags; + +/* -------------------------- Object structure -------------------------------*/ +/* This is the object structure as stored on NAND */ + +typedef struct { + yaffs_ObjectType type; + + /* Apply to everything */ + int parentObjectId; + __u16 sum__NoLongerUsed; /* checksum of name. No longer used */ + char name[YAFFS_MAX_NAME_LENGTH + 1]; + + /* The following apply to directories, files, symlinks - not hard links */ + __u32 yst_mode; /* protection */ + +#ifdef CONFIG_YAFFS_WINCE + __u32 notForWinCE[5]; +#else + __u32 yst_uid; + __u32 yst_gid; + __u32 yst_atime; + __u32 yst_mtime; + __u32 yst_ctime; +#endif + + /* File size applies to files only */ + int fileSize; + + /* Equivalent object id applies to hard links only. */ + int equivalentObjectId; + + /* Alias is for symlinks only. */ + char alias[YAFFS_MAX_ALIAS_LENGTH + 1]; + + __u32 yst_rdev; /* device stuff for block and char devices (major/min) */ + +#ifdef CONFIG_YAFFS_WINCE + __u32 win_ctime[2]; + __u32 win_atime[2]; + __u32 win_mtime[2]; +#else + __u32 roomToGrow[6]; + +#endif + __u32 inbandShadowsObject; + __u32 inbandIsShrink; + + __u32 reservedSpace[2]; + int shadowsObject; /* This object header shadows the specified object if > 0 */ + + /* isShrink applies to object headers written when we shrink the file (ie resize) */ + __u32 isShrink; + +} yaffs_ObjectHeader; + +#endif diff --git a/res/nandroid-mobile.sh b/res/nandroid-mobile.sh deleted file mode 100755 index 9a9a824..0000000 --- a/res/nandroid-mobile.sh +++ /dev/null @@ -1,293 +0,0 @@ -#!/sbin/sh - -# nandroid v2.1 - an Android backup tool for the G1 by infernix and brainaid - -# Requirements: - -# - a modded android in recovery mode (JF 1.3 will work by default) -# - adb shell as root in recovery mode if not using a pre-made recovery image -# - busybox in recovery mode -# - dump_image-arm-uclibc compiled and in path on phone -# - mkyaffs2image-arm-uclibc compiled and installed in path on phone - -# Reference data: - -# dev: size erasesize name -#mtd0: 00040000 00020000 "misc" -#mtd1: 00500000 00020000 "recovery" -#mtd2: 00280000 00020000 "boot" -#mtd3: 04380000 00020000 "system" -#mtd4: 04380000 00020000 "cache" -#mtd5: 04ac0000 00020000 "userdata" -#mtd6 is everything, dump splash1 with: dd if=/dev/mtd/mtd6ro of=/sdcard/splash1.img skip=19072 bs=2048 count=150 - -# We don't dump misc or cache because they do not contain any useful data that we are aware of at this time. - -# Logical steps (v2.1): -# -# 0. test for a target dir and the various tools needed, if not found then exit with error. -# 1. check "adb devices" for a device in recovery mode. set DEVICEID variable to the device ID. abort when not found. -# 2. mount system and data partitions read-only, set up adb portforward and create destdir -# 3. check free space on /cache, exit if less blocks than 20MB free -# 4. push required tools to device in /cache -# 5 for partitions boot recovery misc: -# 5a get md5sum for content of current partition *on the device* (no data transfered) -# 5b while MD5sum comparison is incorrect (always is the first time): -# 5b1 dump current partition to a netcat session -# 5b2 start local netcat to dump image to current dir -# 5b3 compare md5sums of dumped data with dump in current dir. if correct, contine, else restart the loop (6b1) -# 6 for partitions system data: -# 6a get md5sum for tar of content of current partition *on the device* (no data transfered) -# 6b while MD5sum comparison is incorrect (always is the first time): -# 6b1 tar current partition to a netcat session -# 6b2 start local netcat to dump tar to current dir -# 6b3 compare md5sums of dumped data with dump in current dir. if correct, contine, else restart the loop (6b1) -# 6c if i'm running as root: -# 6c1 create a temp dir using either tempdir command or the deviceid in /tmp -# 6c2 extract tar to tempdir -# 6c3 invoke mkyaffs2image to create the img -# 6c4 clean up -# 7. remove tools from device /cache -# 8. umount system and data on device -# 9. print success. - - -DEVICEID=foo -RECOVERY=foo - -echo "nandroid-mobile v2.1" - - -if [ "$1" == "" ]; then - echo "Usage: $0 {backup|restore} [/path/to/nandroid/backup/]" - echo "- backup will store a full system backup on /sdcard/nandroid/$DEVICEID" - echo "- restore path will restore the last made backup for boot, system, recovery and data" - exit 0 -fi - -case $1 in - backup) - mkyaffs2image=`which mkyaffs2image` - if [ "$mkyaffs2image" == "" ]; then - mkyaffs2image=`which mkyaffs2image-arm-uclibc` - if [ "$mkyaffs2image" == "" ]; then - echo "error: mkyaffs2image or mkyaffs2image-arm-uclibc not found in path" - exit 1 - fi - fi - dump_image=`which dump_image` - if [ "$dump_image" == "" ]; then - dump_image=`which dump_image-arm-uclibc` - if [ "$dump_image" == "" ]; then - echo "error: dump_image or dump_image-arm-uclibc not found in path" - exit 1 - fi - fi - break - ;; - restore) - flash_image=`which flash_image` - if [ "$flash_image" == "" ]; then - flash_image=`which flash_image-arm-uclibc` - if [ "$flash_image" == "" ]; then - echo "error: flash_image or flash_image-arm-uclibc not found in path" - exit 1 - fi - fi - break - ;; -esac - -# 1 -DEVICEID=`cat /proc/cmdline | sed "s/.*serialno=//" | cut -d" " -f1` -RECOVERY=`cat /proc/cmdline | grep "androidboot.mode=recovery"` -if [ "$RECOVERY" == "foo" ]; then - echo "error: not running in recovery mode, aborting" - exit 1 -fi -if [ "$DEVICEID" == "foo" ]; then - echo "error: device id not found in /proc/cmdline, aborting" - exit 1 -fi -if [ ! "`id -u 2>/dev/null`" == "0" ]; then - if [ "`whoami 2>&1 | grep 'uid 0'`" == "" ]; then - echo "error: must run as root, aborting" - exit 1 - fi -fi - - -case $1 in - restore) - ENERGY=`cat /sys/class/power_supply/battery/capacity` - if [ "`cat /sys/class/power_supply/battery/status`" == "Charging" ]; then - ENERGY=100 - fi - if [ ! $ENERGY -ge 30 ]; then - echo "Error: not enough battery power" - echo "Connect charger or USB power and try again" - exit 1 - fi - RESTOREPATH=$2 - if [ ! -f $RESTOREPATH/nandroid.md5 ]; then - echo "error: $RESTOREPATH/nandroid.md5 not found, cannot verify backup data" - exit 1 - fi - umount /system 2>/dev/null - umount /data 2>/dev/null - if [ ! "`mount | grep data`" == "" ]; then - echo "error: unable to umount /data, aborting" - exit 1 - fi - if [ ! "`mount | grep system`" == "" ]; then - echo "error: unable to umount /system, aborting" - exit 1 - fi - - echo "Verifying backup images..." - CWD=$PWD - cd $RESTOREPATH - md5sum -c nandroid.md5 - if [ $? -eq 1 ]; then - echo "error: md5sum mismatch, aborting" - exit 1 - fi - for image in boot recovery; do - echo "Flashing $image..." - $flash_image $image $image.img - done - echo "Flashing system and data not currently supported" - echo "Restore done" - exit 0 - ;; - backup) - break - ;; - *) - echo "Usage: $0 {backup|restore} [/path/to/nandroid/backup/]" - echo "- backup will store a full system backup on /sdcard/nandroid/$DEVICEID" - echo "- restore path will restore the last made backup for boot, system, recovery and data" - exit 1 - ;; -esac - -# 2. -echo "mounting system and data read-only, sdcard read-write" -umount /system 2>/dev/null -umount /data 2>/dev/null -umount /sdcard 2>/dev/null -mount -o ro /system || FAIL=1 -mount -o ro /data || FAIL=2 -mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=3 -case $FAIL in - 1) echo "Error mounting system read-only"; umount /system /data /sdcard; exit 1;; - 2) echo "Error mounting data read-only"; umount /system /data /sdcard; exit 1;; - 3) echo "Error mounting sdcard read-write"; umount /system /data /sdcard; exit 1;; -esac - -TIMESTAMP="`date +%Y%m%d-%H%M`" -DESTDIR="/sdcard/nandroid/$DEVICEID/$TIMESTAMP" -if [ ! -d $DESTDIR ]; then - mkdir -p $DESTDIR - if [ ! -d $DESTDIR ]; then - echo "error: cannot create $DESTDIR" - umount /system 2>/dev/null - umount /data 2>/dev/null - umount /sdcard 2>/dev/null - exit 1 - fi -else - touch $DESTDIR/.nandroidwritable - if [ ! -e $DESTDIR/.nandroidwritable ]; then - echo "error: cannot write to $DESTDIR" - umount /system 2>/dev/null - umount /data 2>/dev/null - umount /sdcard 2>/dev/null - exit 1 - fi - rm $DESTDIR/.nandroidwritable -fi - -# 3. -echo "checking free space on sdcard" -FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" -# we need about 130MB for the dump -if [ $FREEBLOCKS -le 130000 ]; then - echo "error: not enough free space available on sdcard (need 130mb), aborting." - umount /system 2>/dev/null - umount /data 2>/dev/null - umount /sdcard 2>/dev/null - exit 1 -fi - - - -if [ -e /dev/mtd/mtd6ro ]; then - echo -n "Dumping splash1 from device over tcp to $DESTDIR/splash1.img..." - dd if=/dev/mtd/mtd6ro of=$DESTDIR/splash1.img skip=19072 bs=2048 count=150 2>/dev/null - echo "done" - sleep 1s - echo -n "Dumping splash2 from device over tcp to $DESTDIR/splash2.img..." - dd if=/dev/mtd/mtd6ro of=$DESTDIR/splash2.img skip=19456 bs=2048 count=150 2>/dev/null - echo "done" -fi - - -# 5. -for image in boot recovery misc; do - # 5a - DEVICEMD5=`$dump_image $image - | md5sum | awk '{ print $1 }'` - sleep 1s - MD5RESULT=1 - # 5b - echo -n "Dumping $image to $DESTDIR/$image.img..." - ATTEMPT=0 - while [ $MD5RESULT -eq 1 ]; do - let ATTEMPT=$ATTEMPT+1 - # 5b1 - $dump_image $image $DESTDIR/$image.img - sync - # 5b3 - echo "${DEVICEMD5} $DESTDIR/$image.img" | md5sum -c -s - - if [ $? -eq 1 ]; then - true - else - MD5RESULT=0 - fi - if [ "$ATTEMPT" == "5" ]; then - echo "fatal error while trying to dump $image, aborting" - umount /system - umount /data - umount /sdcard - exit 1 - fi - done - echo "done" -done - -# 6 -for image in system data cache; do - # 6a - echo -n "Dumping $image to $DESTDIR/$image.img..." - $mkyaffs2image /$image $DESTDIR/$image.img - sync - echo "done" -done - - -# 7. -echo -n "generating md5sum file..." -CWD=$PWD -cd $DESTDIR -md5sum *img > nandroid.md5 -cd $CWD -echo "done" - -# 8. -echo "unmounting system, data and sdcard" -umount /system -umount /data -umount /sdcard - -# 9. -echo "Backup successful." From c788c263973e5dd951068a8e5e074c06d72d4366 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Sat, 20 Feb 2010 17:25:03 -0800 Subject: [PATCH 013/233] more fixes --- mtdutils/Android.mk | 14 +- nandroid/Android.mk | 10 +- nandroid/nandroid-mobile.sh | 1663 ++--------------------------------- 3 files changed, 100 insertions(+), 1587 deletions(-) diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk index b6a5ae4..ff4ab3f 100644 --- a/mtdutils/Android.mk +++ b/mtdutils/Android.mk @@ -20,8 +20,15 @@ LOCAL_STATIC_LIBRARIES := libmtdutils LOCAL_SHARED_LIBRARIES := libcutils libc include $(BUILD_EXECUTABLE) +include $(CLEAR_VARS) +LOCAL_SRC_FILES := dump_image.c mtdutils.c mounts.c +LOCAL_MODULE := dump_image +LOCAL_MODULE_TAGS := eng +include $(BUILD_EXECUTABLE) + include $(CLEAR_VARS) LOCAL_SRC_FILES := flash_image.c +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLE LOCAL_MODULE := recovery_flash_image LOCAL_MODULE_TAGS := eng LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc @@ -33,6 +40,7 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SRC_FILES := dump_image.c mtdutils.c mounts.c +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLE LOCAL_MODULE := recovery_dump_image LOCAL_MODULE_TAGS := eng LOCAL_STATIC_LIBRARIES := libcutils libc @@ -42,11 +50,5 @@ LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin ADDITIONAL_RECOVERY_EXECUTABLES += recovery_dump_image include $(BUILD_EXECUTABLE) -include $(CLEAR_VARS) -LOCAL_SRC_FILES := dump_image.c mtdutils.c mounts.c -LOCAL_MODULE := dump_image -LOCAL_MODULE_TAGS := eng -include $(BUILD_EXECUTABLE) - endif # TARGET_ARCH == arm endif # !TARGET_SIMULATOR diff --git a/nandroid/Android.mk b/nandroid/Android.mk index ae2e7fd..79acc32 100644 --- a/nandroid/Android.mk +++ b/nandroid/Android.mk @@ -4,7 +4,7 @@ ifeq ($(TARGET_ARCH),arm) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := recovery_nandroid -LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLE LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin LOCAL_SRC_FILES := nandroid-mobile.sh LOCAL_MODULE_STEM := nandroid-mobile.sh @@ -14,10 +14,18 @@ include $(BUILD_PREBUILT) include $(CLEAR_VARS) LOCAL_MODULE := recovery_unyaffs LOCAL_MODULE_STEM := unyaffs +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLE LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_SRC_FILES := unyaffs.c LOCAL_STATIC_LIBRARIES := libc libcutils LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_SUBDIR := recovery +ADDITIONAL_RECOVERY_EXECUTABLES += recovery_unyaffs +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_MODULE := unyaffs +LOCAL_SRC_FILES := unyaffs.c include $(BUILD_EXECUTABLE) endif # TARGET_ARCH == arm diff --git a/nandroid/nandroid-mobile.sh b/nandroid/nandroid-mobile.sh index b5cae3d..9a9a824 100755 --- a/nandroid/nandroid-mobile.sh +++ b/nandroid/nandroid-mobile.sh @@ -1,23 +1,6 @@ #!/sbin/sh -# nandroid v2.2.2 - an Android backup tool for the G1 by infernix and brainaid -# restore capability added by cyanogen - -# pensive modified to allow to add prefixes to backups, and to restore specific backups -# pensive added the ability to exclude various images from the restore/backup operations, allows to preserve the newer -# recovery image if an older backup is being restored or to preserve user data. Also, saves space by not backing up -# partitions which change rarely. -# pensive added compressing backups and restoring compressed backups -# pensive added fetching system updates directly from the web into /sdcard/update.zip -# pensive added fetching system updates directly from the web into /cache and applying it. -# pensive added moving *update*.zip from /sdcard/download where a browser puts it to /sdcard/update.zip -# pensive added deletion of stale backups -# pensive added backup for ext2 partition on the sdcard to switch roms -# pensive added composite options --save NAME and --switchto NAME to switch ROMS -# pensive added list backup anywhere on the sdcard -# pensive added list updates (more precisely *.zip) anywhere on the sdcard -# Amon_RA : ext restore -> added check if ext backup is existing -# Amon_RA : ext restore -> added check if ext parition is existing +# nandroid v2.1 - an Android backup tool for the G1 by infernix and brainaid # Requirements: @@ -26,10 +9,6 @@ # - busybox in recovery mode # - dump_image-arm-uclibc compiled and in path on phone # - mkyaffs2image-arm-uclibc compiled and installed in path on phone -# - flash_image-arm-uclibc compiled and in path on phone -# - unyaffs-arm-uclibc compiled and in path on phone -# - for [de]compression needs gzip or bzip2, part of the busybox -# - wget for the wireless updates # Reference data: @@ -44,7 +23,7 @@ # We don't dump misc or cache because they do not contain any useful data that we are aware of at this time. -# Logical steps (v2.2.1): +# Logical steps (v2.1): # # 0. test for a target dir and the various tools needed, if not found then exit with error. # 1. check "adb devices" for a device in recovery mode. set DEVICEID variable to the device ID. abort when not found. @@ -76,612 +55,23 @@ DEVICEID=foo RECOVERY=foo -SUBNAME="" -NORECOVERY=0 -NOBOOT=0 -NODATA=0 -NOSYSTEM=0 -NOMISC=0 -NOCACHE=0 -NOSPLASH1=0 -NOSPLASH2=0 -EXT2=0 - -COMPRESS=0 -GETUPDATE=0 -RESTORE=0 -BACKUP=0 -DELETE=0 -WEBGET=0 -LISTBACKUP=0 -LISTUPDATE=0 -AUTOREBOOT=0 -AUTOAPPLY=0 -ITSANUPDATE=0 -ITSANIMAGE=0 -WEBGETSOURCE="" -WEBGETTARGET="/sdcard" - -DEFAULTUPDATEPATH="/sdcard/download" - -DEFAULTWEBUPDATE=http://n0rp.chemlab.org/android/update-cm-3.6.8.1-signed.zip -# There really should be a clone link always pointing to the latest -#DEFAULTWEBUPDATE="http://n0rp.chemlab.org/android/latestupdate-signed.zip" -DEFAULTWEBIMAGE=http://n0rp.chemlab.org/android/cm-recovery-1.4-signed.zip - -# Set up the default (for pensive at least) nameservers -NAMESERVER1=64.46.128.3 -NAMESERVER2=64.46.128.4 - -# WiFi works, rmnet0 setup ??? -# Do not know how to start the rmnet0 interface in recovery -# If in normal mode "ifconfig rmnet0 down" kills rild too -# /system/bin/rild& exits immediately, todo? +echo "nandroid-mobile v2.1" -DEVICEID=`cat /proc/cmdline | sed "s/.*serialno=//" | cut -d" " -f1` - -# This is the default repository for backups -BACKUPPATH="/sdcard/nandroid/$DEVICEID" - - -# Boot, Cache, Data, Ext2, Misc, Recovery, System, Splash1, Splash2 -# BACKUPLEGEND, If all the partitions are backed up it should be "CBDEMRS12" -# Enables the user to figure at a glance what is in the backup -BACKUPLEGEND="" - -# Normally we want tar to be verbose for confidence building. -TARFLAGS="v" - -DEFAULTCOMPRESSOR=gzip -DEFAULTEXT=.gz -DEFAULTLEVEL="-1" - -ASSUMEDEFAULTUSERINPUT=0 - -echo2log() -{ - if [ -e /cache/recovery/log ]; then - echo $1 >>/cache/recovery/log - else - echo $1 >/cache/recovery/log - fi -} - -batteryAtLeast() -{ - REQUIREDLEVEL=$1 - ENERGY=`cat /sys/class/power_supply/battery/capacity` - if [ "`cat /sys/class/power_supply/battery/status`" == "Charging" ]; then - ENERGY=100 - fi - if [ ! $ENERGY -ge $REQUIREDLEVEL ]; then - $ECHO "Error: not enough battery power, need at least $REQUIREDLEVEL%." - $ECHO "Connect charger or USB power and try again" - exit 1 - fi -} - -if [ "`echo $0 | grep /sbin/nandroid-mobile.sh`" == "" ]; then - cp $0 /sbin - chmod 755 /sbin/`basename $0` - exec /sbin/`basename $0` $@ +if [ "$1" == "" ]; then + echo "Usage: $0 {backup|restore} [/path/to/nandroid/backup/]" + echo "- backup will store a full system backup on /sdcard/nandroid/$DEVICEID" + echo "- restore path will restore the last made backup for boot, system, recovery and data" + exit 0 fi - -# Hm, have to handle old options for the current UI case $1 in - restore) - shift - RESTORE=1 - ;; - backup) - shift - BACKUP=1 - ;; - --) - ;; -esac - -ECHO=echo -OUTPUT="" - -for option in $(getopt --name="nandroid-mobile v2.2.2" -l norecovery -l noboot -l nodata -l nosystem -l nocache -l nomisc -l nosplash1 -l nosplash2 -l subname: -l backup -l restore -l compress -l getupdate -l delete -l path -l webget: -l webgettarget: -l nameserver: -l nameserver2: -l bzip2: -l defaultinput -l autoreboot -l autoapplyupdate -l ext2 -l save: -l switchto: -l listbackup -l listupdate -l silent -l quiet -l help -- "cbruds:p:eql" "$@"); do - case $option in - --silent) - ECHO=echo2log - ASSUMEDEFAULTUSERINPUT=1 - TARFLAGS="" - OUTPUT=>>/cache/recovery/log - shift - ;; - --quiet) - ECHO=echo2log - ASSUMEDEFAULTUSERINPUT=1 - TARFLAGS="" - OUTPUT=>>/cache/recovery/log - shift - ;; - -q) - ECHO=echo2log - ASSUMEDEFAULTUSERINPUT=1 - TARFLAGS="" - OUTPUT=>>/cache/recovery/log - shift - ;; - --help) - ECHO=echo - $ECHO "Usage: $0 {--backup|--restore|--getupdate|--delete|--compress|--bzip2:ARG|--webget:URL|--listbackup|--listupdate} [options]" - $ECHO "" - $ECHO "--help Display this help" - $ECHO "" - $ECHO "-s | --subname: SUBSTRING In case of --backup the SUBSTRING is" - $ECHO " the prefix used with backup name" - $ECHO " in case of --restore or -c|--compress|--bzip2 or" - $ECHO " --delete SUBSTRING specifies backups on which to" - $ECHO " operate" - $ECHO "" - $ECHO "-u | --getupdate Will search /sdcard/download for files named" - $ECHO " *update*.zip, will prompt the user" - $ECHO " to narrow the choice if more than one is found," - $ECHO " and then move the latest, if not unique," - $ECHO " to sdcard root /sdcard with the update.zip name" - $ECHO " It is assumed the browser was used to put the *.zip" - $ECHO " in the /sdcard/download folder. -p|--path option" - $ECHO " would override /sdcard/download with the path of your" - $ECHO " choosing." - $ECHO "" - $ECHO "-p | --path DIR Requires an ARGUMENT, which is the path to where " - $ECHO " the backups are stored, can be used" - $ECHO " when the default path /sdcard/nandroid/$DEVICEID " - $ECHO " needs to be changed" - $ECHO "" - $ECHO "-b | --backup Will store a full system backup on $BACKUPPATH" - $ECHO " One can suppress backup of any image however with options" - $ECHO " starting with --no[partionname]" - $ECHO "" - $ECHO "-e | --ext2 Preserve the contents of the ext2 partition along with" - $ECHO " the other partitions being backed up, to easily switch roms." - $ECHO "" - $ECHO "-r | --restore Will restore the last made backup which matches --subname" - $ECHO " ARGUMENT for boot, system, recovery and data" - $ECHO " unless suppressed by other options" - $ECHO " if no --subname is supplied and the user fails to" - $ECHO " provide any input then the latest backup is used" - $ECHO " When restoring compressed backups, the images will remain" - $ECHO " decompressed after the restore, you need to use -c|-compress" - $ECHO " or --bzip2 to compress the backup again" - $ECHO "" - $ECHO "-d | --delete Will remove backups whose names match --subname ARGUMENT" - $ECHO " Will allow to narrow down, will ask if the user is certain." - $ECHO " Removes one backup at a time, repeat to remove multiple backups" - $ECHO "" - $ECHO "-c | --compress Will operate on chosen backups to compress image files," - $ECHO " resulting in saving of about 40MB out of 90+mb," - $ECHO " i.e. up to 20 backups can be stored in 1 GIG, if this " - $ECHO " option is turned on with --backup, the resulting backup will" - $ECHO " be compressed, no effect if restoring since restore will" - $ECHO " automatically uncompress compressed images if space is available" - $ECHO "" - $ECHO "--bzip2: -# Turns on -c|--compress and uses bzip2 for compression instead" - $ECHO " of gzip, requires an ARG -[1-9] compression level" - $ECHO "" - $ECHO "--webget: URL|\"\" Requires an argument, a valid URL for an *update*.zip file" - $ECHO " If a null string is passed then the default URL is used" - $ECHO " Will also create an update.MD5sum file and update.name with the" - $ECHO " original file name." - $ECHO "" - $ECHO "--nameserver: IP addr Provide the first nameserver IP address, to override the default" - $ECHO "" - $ECHO "--nameserver2: IP addr Provide the second nameserver IP address, to override the default" - $ECHO "" - $ECHO "--webgettarget: DIR Target directory to deposit the fetched update, default is" - $ECHO " /sdcard" - $ECHO "" - $ECHO "--norecovery Will suppress restore/backup of the recovery partition" - $ECHO " If recovery.img was not part of the backup, no need to use this" - $ECHO " option as the nandroid will not attempt to restore it, same goes" - $ECHO " for all the options below" - $ECHO "" - $ECHO "--noboot Will suppress restore/backup of the boot partition" - $ECHO "" - $ECHO "--nodata Will suppress restore/backup of the data partition" - $ECHO "" - $ECHO "--nosystem Will suppress restore/backup of the system partition" - $ECHO "" - $ECHO "--nocache Will suppress restore/backup of the cache partition" - $ECHO "" - $ECHO "--nomisc Will suppress restore/backup of the misc partition" - $ECHO "" - $ECHO "--nosplash1 Will suppress restore/backup of the splash1 partition" - $ECHO "" - $ECHO "--nosplash2 Will suppress restore/backup of the splash2 partition" - $ECHO "" - $ECHO "--defaultinput Makes nandroid-mobile non-interactive, assumes default" - $ECHO " inputs from the user." - $ECHO "" - $ECHO "--autoreboot Automatically reboot the phone after a backup, especially" - $ECHO " useful when the compression options are on -c|--compress| " - $ECHO " --bzip2 -level since the compression op takes time and" - $ECHO " you may want to go to sleep or do something else, and" - $ECHO " when a backup is over you want the calls and mms coming" - $ECHO " through, right?" - $ECHO "" - $ECHO "--autoapplyupdate Once the specific update is downloaded or chosen from the" - $ECHO " sdcard, apply it immediately. This option is valid as a " - $ECHO " modifier for either --webget or --getupdate options." - $ECHO "" - $ECHO "-e|--ext2 Save the contents of ext2 partition in the backup folder too." - $ECHO " Enables to keep different sets of apps for different ROMS and" - $ECHO " switch easily between them." - $ECHO "" - $ECHO "--save: ROMTAG Preserve EVERYTHING under ROMTAG name, a composite option," - $ECHO " it uses several other options." - $ECHO "" - $ECHO "--switchto: ROMTAG Restores your entire environment including app2sd apps, cache" - $ECHO " to the ROM environment named ROMTAG." - $ECHO "" - $ECHO "-q|--quiet|--silent Direct all the output to the recovery log, and assume default" - $ECHO " user inputs." - $ECHO "" - $ECHO "-l|--listbackup Will search the entire SDCARD for backup folders and will dump" - $ECHO " the list to stdout for use by the UI. Should be run with --silent" - $ECHO "" - $ECHO "--listupdate Will search the entire SDCARD for updates and will dump" - $ECHO " the list to stdout for use by the UI. Should be run with --silent" - exit 0 - ;; - --norecovery) - NORECOVERY=1 - #$ECHO "No recovery" - shift - ;; - --noboot) - NOBOOT=1 - #$ECHO "No boot" - shift - ;; - --nodata) - NODATA=1 - #$ECHO "No data" - shift - ;; - --nosystem) - NOSYSTEM=1 - #$ECHO "No system" - shift - ;; - --nocache) - NOCACHE=1 - #$ECHO "No cache" - shift - ;; - --nomisc) - NOMISC=1 - #$ECHO "No misc" - shift - ;; - --nosplash1) - NOSPLASH1=1 - #$ECHO "No splash1" - shift - ;; - --nosplash2) - NOSPLASH2=1 - #$ECHO "No splash2" - shift - ;; - --backup) - BACKUP=1 - #$ECHO "backup" - shift - ;; - -b) - BACKUP=1 - #$ECHO "backup" - shift - ;; - -e) - EXT2=1 - shift - ;; - --ext2) - EXT2=1 - shift - ;; - --restore) - RESTORE=1 - #$ECHO "restore" - shift - ;; - -r) - RESTORE=1 - #$ECHO "restore" - shift - ;; - --compress) - COMPRESS=1 - #$ECHO "Compress" - shift - ;; - -c) - COMPRESS=1 - #$ECHO "Compress" - shift - ;; - --bzip2) - COMPRESS=1 - DEFAULTCOMPRESSOR=bzip2 - DEFAULTEXT=.bz2 - if [ "$2" == "$option" ]; then - shift - fi - DEFAULTLEVEL="$2" - shift - ;; - --getupdate) - GETUPDATE=1 - shift - ;; - -u) - GETUPDATE=1 - shift - ;; - --subname) - if [ "$2" == "$option" ]; then - shift - fi - #$ECHO $2 - SUBNAME="$2" - shift - ;; - -s) - if [ "$2" == "$option" ]; then - shift - fi - #$ECHO $2 - SUBNAME="$2" - shift - ;; - --path) - if [ "$2" == "$option" ]; then - shift - fi - #$ECHO $2 - BACKUPPATH="$2" - DEFAULTUPDATEPATH="$2" - shift 2 - ;; - -p) - if [ "$2" == "$option" ]; then - shift - fi - #$ECHO $2 - BACKUPPATH="$2" - shift 2 - ;; - --delete) - DELETE=1 - shift - ;; - -d) - DELETE=1 - shift - ;; - --webgettarget) - if [ "$2" == "$option" ]; then - shift - fi - WEBGETTARGET="$2" - shift - ;; - --webget) - if [ "$2" == "$option" ]; then - shift - fi - #$ECHO "WEBGET" - # if the argument is "" stick with the default: /sdcard - if [ ! "$2" == "" ]; then - WEBGETSOURCE="$2" - fi - WEBGET=1 - shift - ;; - --nameserver) - if [ "$2" == "$option" ]; then - shift - fi - NAMESERVER1="$2" - shift - ;; - --nameserver2) - if [ "$2" == "$option" ]; then - shift - fi - NAMESERVER2="$2" - shift - ;; - --defaultinput) - ASSUMEDEFAULTUSERINPUT=1 - shift - ;; - --autoreboot) - AUTOREBOOT=1 - shift - ;; - --autoapplyupdate) - AUTOAPPLY=1 - shift - ;; - --save) - if [ "$2" == "$option" ]; then - shift - fi - SUBNAME="$2" - BACKUP=1 - ASSUMEDEFAULTUSERINPUT=1 - EXT2=1 - COMPRESS=1 - shift - ;; - --switchto) - if [ "$2" == "$option" ]; then - shift - fi - SUBNAME="$2" - RESTORE=1 - ASSUMEDEFAULTUSERINPUT=1 - EXT2=1 - shift - ;; - -l) - shift - LISTBACKUP=1 - ;; - --listbackup) - shift - LISTBACKUP=1 - ;; - --listupdate) - shift - LISTUPDATE=1 - ;; - --) - shift - break - ;; - esac -done - -$ECHO "" -$ECHO "nandroid-mobile v2.2.1" -$ECHO "" - -let OPS=$BACKUP -let OPS=$OPS+$RESTORE -let OPS=$OPS+$DELETE -let OPS=$OPS+$WEBGET -let OPS=$OPS+$GETUPDATE -let OPS=$OPS+$LISTBACKUP -let OPS=$OPS+$LISTUPDATE - -if [ "$OPS" -ge 2 ]; then - ECHO=echo - $ECHO "--backup, --restore, --delete, --webget, --getupdate, --listbackup, --listupdate are mutually exclusive operations." - $ECHO "Please, choose one and only one option!" - $ECHO "Aborting." - exit 1 -fi - -let OPS=$OPS+$COMPRESS - -if [ "$OPS" -lt 1 ]; then - ECHO=echo - $ECHO "Usage: $0 {-b|--backup|-r|--restore|-d|--delete|-u|--getupdate|--webget|-c|--compress|--bzip2 -level|-l|--listbackup|--listupdate} [options]" - $ECHO "At least one operation must be defined, try $0 --help for more information." - exit 0 -fi - - -if [ "$LISTBACKUP" == 1 ]; then - umount /sdcard 2>/dev/null - mount /sdcard 2>/dev/null - CHECK=`mount | grep /sdcard` - if [ "$CHECK" == "" ]; then - echo "Error: sdcard not mounted, aborting." - exit 1 - else - find /sdcard | grep nandroid.md5 | sed s/.gz//g | sed s/.bz2//g | sed s/nandroid.md5//g - umount /sdcard 2>/dev/null - exit 0 - fi -fi - -if [ "$LISTUPDATE" == 1 ]; then - umount /sdcard 2>/dev/null - mount /sdcard 2>/dev/null - CHECK=`mount | grep /sdcard` - if [ "$CHECK" == "" ]; then - echo "Error: sdcard not mounted, aborting." - exit 1 - else - find /sdcard | grep .zip - umount /sdcard 2>/dev/null - exit 0 - fi -fi - -# Make sure it exists -touch /cache/recovery/log - - -if [ "$AUTOAPPLY" == 1 -a "$WEBGET" == 0 -a "$GETUPDATE" == 0 ]; then - ECHO=echo - $ECHO "The --autoapplyupdate option is valid only in conjunction with --webget or --getupdate." - $ECHO "Aborting." - exit 1 -fi - -if [ "$COMPRESS" == 1 ]; then - $ECHO "Compressing with $DEFAULTCOMPRESSOR, level $DEFAULTLEVEL" -fi - -if [ "$WEBGET" == 1 ]; then - $ECHO "Fetching $WEBGETSOURCE to target folder: $WEBGETTARGET" -fi - -if [ ! "$SUBNAME" == "" ]; then - if [ "$BACKUP" == 1 ]; then - if [ "$COMPRESS" == 1 ]; then - $ECHO "Using $SUBNAME- prefix to create a compressed backup folder" - else - $ECHO "Using $SUBNAME- prefix to create a backup folder" - fi - else - if [ "$RESTORE" == 1 -o "$DELETE" == 1 -o "$COMPRESS" == 1 ]; then - $ECHO "Searching for backup directories, matching $SUBNAME, to delete or restore" - $ECHO "or compress" - $ECHO "" - fi - fi -else - if [ "$BACKUP" == 1 ]; then - $ECHO "Using G1 keyboard, enter a prefix substring and then " - $ECHO -n "or just to accept default: " - if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then - read SUBNAME - else - $ECHO "Accepting default." - fi - $ECHO "" - if [ "$COMPRESS" == 1 ]; then - $ECHO "Using $SUBNAME- prefix to create a compressed backup folder" - else - $ECHO "Using $SUBNAME- prefix to create a backup folder" - fi - $ECHO "" - else - if [ "$RESTORE" == 1 -o "$DELETE" == 1 -o "$COMPRESS" == 1 ]; then - $ECHO "Using G1 keyboard, enter a directory name substring and then " - $ECHO -n "to find matches or just to accept default: " - if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then - read SUBNAME - else - $ECHO "Accepting default." - fi - $ECHO "" - $ECHO "Using $SUBNAME string to search for matching backup directories" - $ECHO "" - fi - fi -fi - -if [ "$BACKUP" == 1 ]; then + backup) mkyaffs2image=`which mkyaffs2image` if [ "$mkyaffs2image" == "" ]; then mkyaffs2image=`which mkyaffs2image-arm-uclibc` if [ "$mkyaffs2image" == "" ]; then - $ECHO "error: mkyaffs2image or mkyaffs2image-arm-uclibc not found in path" + echo "error: mkyaffs2image or mkyaffs2image-arm-uclibc not found in path" exit 1 fi fi @@ -689,331 +79,100 @@ if [ "$BACKUP" == 1 ]; then if [ "$dump_image" == "" ]; then dump_image=`which dump_image-arm-uclibc` if [ "$dump_image" == "" ]; then - $ECHO "error: dump_image or dump_image-arm-uclibc not found in path" + echo "error: dump_image or dump_image-arm-uclibc not found in path" exit 1 fi fi -fi - -if [ "$RESTORE" == 1 ]; then + break + ;; + restore) flash_image=`which flash_image` if [ "$flash_image" == "" ]; then flash_image=`which flash_image-arm-uclibc` if [ "$flash_image" == "" ]; then - $ECHO "error: flash_image or flash_image-arm-uclibc not found in path" + echo "error: flash_image or flash_image-arm-uclibc not found in path" exit 1 fi fi - unyaffs=`which unyaffs` - if [ "$unyaffs" == "" ]; then - unyaffs=`which unyaffs-arm-uclibc` - if [ "$unyaffs" == "" ]; then - $ECHO "error: unyaffs or unyaffs-arm-uclibc not found in path" - exit 1 - fi - fi -fi -if [ "$COMPRESS" == 1 ]; then - compressor=`busybox | grep $DEFAULTCOMPRESSOR` - if [ "$compressor" == "" ]; then - $ECHO "Warning: busybox/$DEFAULTCOMPRESSOR is not found" - $ECHO "No compression operations will be performed" - COMPRESS=0 - else - $ECHO "Found $DEFAULTCOMPRESSOR, will compress the backup" - fi -fi + break + ;; +esac # 1 DEVICEID=`cat /proc/cmdline | sed "s/.*serialno=//" | cut -d" " -f1` RECOVERY=`cat /proc/cmdline | grep "androidboot.mode=recovery"` if [ "$RECOVERY" == "foo" ]; then - $ECHO "Error: Must be in recovery mode, aborting" + echo "error: not running in recovery mode, aborting" exit 1 fi if [ "$DEVICEID" == "foo" ]; then - $ECHO "Error: device id not found in /proc/cmdline, aborting" + echo "error: device id not found in /proc/cmdline, aborting" exit 1 fi if [ ! "`id -u 2>/dev/null`" == "0" ]; then if [ "`whoami 2>&1 | grep 'uid 0'`" == "" ]; then - $ECHO "Error: must run as root, aborting" + echo "error: must run as root, aborting" exit 1 fi fi -if [ "$RESTORE" == 1 ]; then - batteryAtLeast 30 -# ENERGY=`cat /sys/class/power_supply/battery/capacity` -# if [ "`cat /sys/class/power_supply/battery/status`" == "Charging" ]; then -# ENERGY=100 -# fi -# if [ ! $ENERGY -ge 30 ]; then -# $ECHO "Error: not enough battery power" -# $ECHO "Connect charger or USB power and try again" -# exit 1 -# fi - - - umount /sdcard 2>/dev/null - mount /sdcard 2>/dev/null - if [ "`mount | grep sdcard`" == "" ]; then - $ECHO "error: unable to mount /sdcard, aborting" +case $1 in + restore) + ENERGY=`cat /sys/class/power_supply/battery/capacity` + if [ "`cat /sys/class/power_supply/battery/status`" == "Charging" ]; then + ENERGY=100 + fi + if [ ! $ENERGY -ge 30 ]; then + echo "Error: not enough battery power" + echo "Connect charger or USB power and try again" exit 1 fi - - # find the latest backup, but show the user other options - $ECHO "" - $ECHO "Looking for the latest backup, will display other choices!" - $ECHO "" - - RESTOREPATH=`ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | tail -1` - $ECHO " " - - if [ "$RESTOREPATH" = "" ]; - then - $ECHO "Error: no backups found" - exit 2 - else - $ECHO "Default backup is the latest: $RESTOREPATH" - $ECHO "" - $ECHO "Other available backups are: " - $ECHO "" - ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | grep -v $RESTOREPATH $OUTPUT - $ECHO "" - $ECHO "Using G1 keyboard, enter a unique name substring to change it and " - $ECHO -n "or just to accept: " - if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then - read SUBSTRING - else - $ECHO "Accepting default." - SUBSTRING="" - fi - $ECHO "" - - if [ ! "$SUBSTRING" == "" ]; then - RESTOREPATH=`ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | grep $SUBSTRING | tail -1` - else - RESTOREPATH=`ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | tail -1` - fi - if [ "$RESTOREPATH" = "" ]; then - $ECHO "Error: no matching backups found, aborting" - exit 2 - fi - fi - - $ECHO "Restore path: $RESTOREPATH" - $ECHO "" - - umount /system /data 2>/dev/null - mount /system 2>/dev/null - mount /data 2>/dev/null - if [ "`mount | grep data`" == "" ]; then - $ECHO "error: unable to mount /data, aborting" + RESTOREPATH=$2 + if [ ! -f $RESTOREPATH/nandroid.md5 ]; then + echo "error: $RESTOREPATH/nandroid.md5 not found, cannot verify backup data" exit 1 fi - if [ "`mount | grep system`" == "" ]; then - $ECHO "error: unable to mount /system, aborting" + umount /system 2>/dev/null + umount /data 2>/dev/null + if [ ! "`mount | grep data`" == "" ]; then + echo "error: unable to umount /data, aborting" + exit 1 + fi + if [ ! "`mount | grep system`" == "" ]; then + echo "error: unable to umount /system, aborting" exit 1 fi + echo "Verifying backup images..." CWD=$PWD cd $RESTOREPATH - - DEFAULTEXT="" - if [ `ls *.bz2 2>/dev/null|wc -l` -ge 1 ]; then - DEFAULTCOMPRESSOR=bzip2 - DEFAULTDECOMPRESSOR=bunzip2 - DEFAULTEXT=.bz2 - fi - if [ `ls *.gz 2>/dev/null|wc -l` -ge 1 ]; then - DEFAULTCOMPRESSOR=gzip - DEFAULTDECOMPRESSOR=gunzip - DEFAULTEXT=.gz - fi - - if [ ! -f $RESTOREPATH/nandroid.md5$DEFAULTEXT ]; then - $ECHO "error: $RESTOREPATH/nandroid.md5 not found, cannot verify backup data" - exit 1 - fi - - if [ `ls *.bz2 2>/dev/null|wc -l` -ge 1 -o `ls *.gz 2>/dev/null|wc -l` -ge 1 ]; then - $ECHO "This backup is compressed with $DEFAULTCOMPRESSOR." - - # Make sure that $DEFAULT[DE]COMPRESSOR exists - if [ `busybox | grep $DEFAULTCOMPRESSOR | wc -l` -le 0 -a\ - `busybox | grep $DEFAULTDECOMPRESSOR | wc -l` -le 0 ]; then - - $ECHO "You do not have either the $DEFAULTCOMPRESSOR or the $DEFAULTDECOMPRESSOR" - $ECHO "to unpack this backup, cleaning up and aborting!" - umount /system 2>/dev/null - umount /data 2>/dev/null - umount /sdcard 2>/dev/null - exit 1 - fi - $ECHO "Checking free space /sdcard for the decompression operation." - FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" - # we need about 100MB for gzip to uncompress the files - if [ $FREEBLOCKS -le 100000 ]; then - $ECHO "Error: not enough free space available on sdcard (need about 100mb)" - $ECHO "to perform restore from the compressed images, aborting." - umount /system 2>/dev/null - umount /data 2>/dev/null - umount /sdcard 2>/dev/null - exit 1 - fi - $ECHO "Decompressing images, please wait...." - $ECHO "" - # Starting from the largest while we still have more space to reduce - # space requirements - $DEFAULTCOMPRESSOR -d `ls -S *$DEFAULTEXT` - $ECHO "Backup images decompressed" - $ECHO "" - fi - - $ECHO "Verifying backup images..." md5sum -c nandroid.md5 if [ $? -eq 1 ]; then - $ECHO "Error: md5sum mismatch, aborting" + echo "error: md5sum mismatch, aborting" exit 1 fi - - if [ `ls boot* 2>/dev/null | wc -l` == 0 ]; then - NOBOOT=1 - fi - if [ `ls recovery* 2>/dev/null | wc -l` == 0 ]; then - NORECOVERY=1 - fi - if [ `ls data* 2>/dev/null | wc -l` == 0 ]; then - NODATA=1 - fi - if [ `ls system* 2>/dev/null | wc -l` == 0 ]; then - NOSYSTEM=1 - fi - # Amon_RA : If there's no ext backup set EXT2 to 0 so ext2 restore doesn't start - if [ `ls ext2* 2>/dev/null | wc -l` == 0 ]; then - EXT2=0 - fi - for image in boot recovery; do - if [ "$NOBOOT" == "1" -a "$image" == "boot" ]; then - $ECHO "" - $ECHO "Not flashing boot image!" - $ECHO "" - continue - fi - if [ "$NORECOVERY" == "1" -a "$image" == "recovery" ]; then - $ECHO "" - $ECHO "Not flashing recovery image!" - $ECHO "" - continue - fi - $ECHO "Flashing $image..." - $flash_image $image $image.img $OUTPUT - done - - for image in data system; do - if [ "$NODATA" == "1" -a "$image" == "data" ]; then - $ECHO "" - $ECHO "Not restoring data image!" - $ECHO "" - continue - fi - if [ "$NOSYSTEM" == "1" -a "$image" == "system" ]; then - $ECHO "" - $ECHO "Not restoring system image!" - $ECHO "" - continue - fi - $ECHO "Erasing /$image..." - cd /$image - rm -rf * 2>/dev/null - $ECHO "Unpacking $image image..." - $unyaffs $RESTOREPATH/$image.img $OUTPUT - cd / - sync - umount /$image + echo "Flashing $image..." + $flash_image $image $image.img done - - if [ "$EXT2" == 1 ]; then - # Amon_RA : Check if there's an ext partition before starting to restore - if [ -e /dev/block/mmcblk0p2 ]; then - $ECHO "Restoring the ext2 contents." - CWD=`pwd` - cd / - - if [ `mount | grep /system | wc -l` == 0 ]; then - mount /system - else - mount -o rw,remount /system - fi - - if [ `mount | grep /system/sd | wc -l` == 0 ]; then - mount /system/sd - fi - - cd $CWD - CHECK=`mount | grep /system/sd` - - if [ "$CHECK" == "" ]; then - $ECHO "Warning: --ext2 specified but unable to mount the ext2 partition." - $ECHO "Warning: your phone may be in an inconsistent state on reboot." - exit 1 - else - CWD=`pwd` - cd /system/sd - # Depending on whether the ext2 backup is compressed we do either or. - if [ -e $RESTOREPATH/ext2.tar ]; then - rm -rf * 2>/dev/null - tar -x$TARFLAGS -f $RESTOREPATH/ext2.tar - else - if [ -e $RESTOREPATH/ext2.tgz ]; then - rm -rf * 2>/dev/null - tar -x$TARFLAGS -zf $RESTOREPATH/ext2.tgz - else - if [ -e $RESTOREPATH/ext2.tar.bz2 ]; then - rm -rf * 2>/dev/null - tar -x$TARFLAGS -jf $RESTOREPATH/ext2.tar.bz2 - else - $ECHO "Warning: --ext2 specified but cannot find the ext2 backup." - $ECHO "Warning: your phone may be in an inconsistent state on reboot." - fi - fi - fi - cd $CWD - sync - umount /system/sd - umount /system - - fi - else - # Amon_RA : Just display a warning - $ECHO "Warning: --ext2 specified but ext2 partition present on sdcard" - $ECHO "Warning: your phone may be in an inconsistent state on reboot." - fi - fi - $ECHO "Restore done" + echo "Flashing system and data not currently supported" + echo "Restore done" exit 0 -fi + ;; + backup) + break + ;; + *) + echo "Usage: $0 {backup|restore} [/path/to/nandroid/backup/]" + echo "- backup will store a full system backup on /sdcard/nandroid/$DEVICEID" + echo "- restore path will restore the last made backup for boot, system, recovery and data" + exit 1 + ;; +esac # 2. -if [ "$BACKUP" == 1 ]; then - - if [ "$COMPRESS" == 1 ]; then - ENERGY=`cat /sys/class/power_supply/battery/capacity` - if [ "`cat /sys/class/power_supply/battery/status`" == "Charging" ]; then - ENERGY=100 - fi - if [ ! $ENERGY -ge 30 ]; then - $ECHO "Warning: Not enough battery power to perform compression." - COMPRESS=0 - $ECHO "Turning off compression option, you can compress the backup later" - $ECHO "with the compression options." - fi - fi - -$ECHO "mounting system and data read-only, sdcard read-write" +echo "mounting system and data read-only, sdcard read-write" umount /system 2>/dev/null umount /data 2>/dev/null umount /sdcard 2>/dev/null @@ -1021,61 +180,17 @@ mount -o ro /system || FAIL=1 mount -o ro /data || FAIL=2 mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=3 case $FAIL in - 1) $ECHO "Error mounting system read-only"; umount /system /data /sdcard; exit 1;; - 2) $ECHO "Error mounting data read-only"; umount /system /data /sdcard; exit 1;; - 3) $ECHO "Error mounting sdcard read-write"; umount /system /data /sdcard; exit 1;; + 1) echo "Error mounting system read-only"; umount /system /data /sdcard; exit 1;; + 2) echo "Error mounting data read-only"; umount /system /data /sdcard; exit 1;; + 3) echo "Error mounting sdcard read-write"; umount /system /data /sdcard; exit 1;; esac -if [ ! "$SUBNAME" == "" ]; then - SUBNAME=$SUBNAME- -fi - -# Identify the backup with what partitions have been backed up -if [ "$NOBOOT" == 0 ]; then - BACKUPLEGEND=$BACKUPLEGEND"B" -fi -if [ "$NOCACHE" == 0 ]; then - BACKUPLEGEND=$BACKUPLEGEND"C" -fi -if [ "$NODATA" == 0 ]; then - BACKUPLEGEND=$BACKUPLEGEND"D" -fi -if [ "$EXT2" == 1 ]; then - BACKUPLEGEND=$BACKUPLEGEND"E" -fi -if [ "$NOMISC" == 0 ]; then - BACKUPLEGEND=$BACKUPLEGEND"M" -fi -if [ "$NORECOVERY" == 0 ]; then - BACKUPLEGEND=$BACKUPLEGEND"R" -fi -if [ "$NOSYSTEM" == 0 ]; then - BACKUPLEGEND=$BACKUPLEGEND"S" -fi - -if [ ! -e /dev/mtd/mtd6ro ]; then - NOSPLASH1=1 - NOSPLASH2=1 -fi - -if [ "$NOSPLASH1" == 0 ]; then - BACKUPLEGEND=$BACKUPLEGEND"1" -fi -if [ "$NOSPLASH2" == 0 ]; then - BACKUPLEGEND=$BACKUPLEGEND"2" -fi - -if [ ! "$BACKUPLEGEND" == "" ]; then - BACKUPLEGEND=$BACKUPLEGEND- -fi - - TIMESTAMP="`date +%Y%m%d-%H%M`" -DESTDIR="$BACKUPPATH/$SUBNAME$BACKUPLEGEND$TIMESTAMP" +DESTDIR="/sdcard/nandroid/$DEVICEID/$TIMESTAMP" if [ ! -d $DESTDIR ]; then mkdir -p $DESTDIR if [ ! -d $DESTDIR ]; then - $ECHO "error: cannot create $DESTDIR" + echo "error: cannot create $DESTDIR" umount /system 2>/dev/null umount /data 2>/dev/null umount /sdcard 2>/dev/null @@ -1084,7 +199,7 @@ if [ ! -d $DESTDIR ]; then else touch $DESTDIR/.nandroidwritable if [ ! -e $DESTDIR/.nandroidwritable ]; then - $ECHO "error: cannot write to $DESTDIR" + echo "error: cannot write to $DESTDIR" umount /system 2>/dev/null umount /data 2>/dev/null umount /sdcard 2>/dev/null @@ -1094,11 +209,11 @@ else fi # 3. -$ECHO "checking free space on sdcard" +echo "checking free space on sdcard" FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" # we need about 130MB for the dump if [ $FREEBLOCKS -le 130000 ]; then - $ECHO "Error: not enough free space available on sdcard (need 130mb), aborting." + echo "error: not enough free space available on sdcard (need 130mb), aborting." umount /system 2>/dev/null umount /data 2>/dev/null umount /sdcard 2>/dev/null @@ -1108,683 +223,71 @@ fi if [ -e /dev/mtd/mtd6ro ]; then - if [ "$NOSPLASH1" == 0 ]; then - $ECHO -n "Dumping splash1 from device over tcp to $DESTDIR/splash1.img..." + echo -n "Dumping splash1 from device over tcp to $DESTDIR/splash1.img..." dd if=/dev/mtd/mtd6ro of=$DESTDIR/splash1.img skip=19072 bs=2048 count=150 2>/dev/null - $ECHO "done" + echo "done" sleep 1s - else - $ECHO "Dump of the splash1 image suppressed." - fi - if [ "$NOSPLASH2" == 0 ]; then - $ECHO -n "Dumping splash2 from device over tcp to $DESTDIR/splash2.img..." + echo -n "Dumping splash2 from device over tcp to $DESTDIR/splash2.img..." dd if=/dev/mtd/mtd6ro of=$DESTDIR/splash2.img skip=19456 bs=2048 count=150 2>/dev/null - $ECHO "done" - else - $ECHO "Dump of the splash2 image suppressed." - fi + echo "done" fi # 5. for image in boot recovery misc; do - - case $image in - boot) - if [ "$NOBOOT" == 1 ]; then - $ECHO "Dump of the boot partition suppressed." - continue - fi - ;; - recovery) - if [ "$NORECOVERY" == 1 ]; then - $ECHO "Dump of the recovery partition suppressed." - continue - fi - ;; - misc) - if [ "$NOMISC" == 1 ]; then - $ECHO "Dump of the misc partition suppressed." - continue - fi - ;; - esac - # 5a DEVICEMD5=`$dump_image $image - | md5sum | awk '{ print $1 }'` sleep 1s MD5RESULT=1 # 5b - $ECHO -n "Dumping $image to $DESTDIR/$image.img..." + echo -n "Dumping $image to $DESTDIR/$image.img..." ATTEMPT=0 while [ $MD5RESULT -eq 1 ]; do let ATTEMPT=$ATTEMPT+1 # 5b1 - $dump_image $image $DESTDIR/$image.img $OUTPUT + $dump_image $image $DESTDIR/$image.img sync # 5b3 - echo "${DEVICEMD5} $DESTDIR/$image.img" | md5sum -c -s - $OUTPUT + echo "${DEVICEMD5} $DESTDIR/$image.img" | md5sum -c -s - if [ $? -eq 1 ]; then true else MD5RESULT=0 fi if [ "$ATTEMPT" == "5" ]; then - $ECHO "Fatal error while trying to dump $image, aborting." + echo "fatal error while trying to dump $image, aborting" umount /system umount /data umount /sdcard exit 1 fi done - $ECHO "done" + echo "done" done # 6 for image in system data cache; do - case $image in - system) - if [ "$NOSYSTEM" == 1 ]; then - $ECHO "Dump of the system partition suppressed." - continue - fi - ;; - data) - if [ "$NODATA" == 1 ]; then - $ECHO "Dump of the data partition suppressed." - continue - fi - ;; - cache) - if [ "$NOCACHE" == 1 ]; then - $ECHO "Dump of the cache partition suppressed." - continue - fi - ;; - esac - # 6a - $ECHO -n "Dumping $image to $DESTDIR/$image.img..." - $mkyaffs2image /$image $DESTDIR/$image.img $OUTPUT + echo -n "Dumping $image to $DESTDIR/$image.img..." + $mkyaffs2image /$image $DESTDIR/$image.img sync - $ECHO "done" + echo "done" done -# Backing up the ext2 partition, not really for the backup but to switch ROMS and apps at the same time. - -if [ "$EXT2" == 1 ]; then - $ECHO "Storing the ext2(Apps, Dalvik-cache) contents in the backup folder." - - CHECK1=`mount | grep /system` - if [ "$CHECK1" == "" ]; then - mount /system 2>/dev/null - fi - CHECK2=`mount | grep /system/sd` - if [ "$CHECK2" == "" ]; then - mount /system/sd 2>/dev/null - fi - - CHECK1=`mount | grep /system` - CHECK2=`mount | grep /system/sd` - if [ "$CHECK1" == "" -o "$CHECK2" == "" ]; then - $ECHO "Warning: --ext2 specified but unable to mount the ext2 partition." - exit 1 - else - - CWD=`pwd` - cd /system/sd - # Depending on the whether we want it compressed we do either or. - if [ "$COMPRESS" == 0 ]; then - tar -cvf $DESTDIR/ext2.tar ./ - else - if [ "$DEFAULTCOMPRESSOR" == "bzip2" ]; then - tar -cvjf $DESTDIR/ext2.tar.bz2 ./ - else - tar -cvzf $DESTDIR/ext2.tgz ./ - fi - fi - cd $CWD - umount /system/sd - fi -fi - # 7. -$ECHO -n "generating md5sum file..." +echo -n "generating md5sum file..." CWD=$PWD cd $DESTDIR md5sum *img > nandroid.md5 - -# 7b. -if [ "$COMPRESS" == 1 ]; then - $ECHO "Compressing the backup, may take a bit of time, please wait..." - $ECHO "checking free space on sdcard for the compression operation." - FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" - # we need about 70MB for the intermediate storage needs - if [ $FREEBLOCKS -le 70000 ]; then - $ECHO "error: not enough free space available on sdcard for compression operation (need 70mb)" - $ECHO "leaving this backup uncompressed." - else - # we are already in $DESTDIR, start compression from the smallest files - # to maximize space for the largest's compression, less likely to fail. - # To decompress reverse the order. - $DEFAULTCOMPRESSOR $DEFAULTLEVEL `ls -S -r * | grep -v ext2` - fi -fi - cd $CWD -$ECHO "done" +echo "done" # 8. -$ECHO "unmounting system, data and sdcard" +echo "unmounting system, data and sdcard" umount /system umount /data umount /sdcard # 9. -$ECHO "Backup successful." -if [ "$AUTOREBOOT" == 1 ]; then - reboot -fi -exit 0 -fi - - -# ----------------------------------GETTING UPDATES DIRECT FROM THE WEB USING WIFI------------- - -if [ "$WEBGET" == 1 ]; then - $ECHO "mounting system and data read-only, sdcard read-write" - umount /system 2>/dev/null - umount /data 2>/dev/null - umount /sdcard 2>/dev/null - - # Since we are in recovery, these file-systems have to be mounted - $ECHO "Mounting /system and /data for starting WiFi" - mount -o ro /system || FAIL=1 - # Need to write to this system to setup nameservers for the wifi - mount -o rw /data || FAIL=2 - mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=3 - - case $FAIL in - 1) $ECHO "Error mounting system read-only"; umount /system /data /sdcard; exit 1;; - 2) $ECHO "Error mounting data read-write"; umount /system /data /sdcard; exit 1;; - 3) $ECHO "Error mounting sdcard read-write"; umount /system /data /sdcard; exit 1;; - esac - - if [ "$WEBGETSOURCE" == "" ]; then - # Set the URL to the current latest update - if [ "$ITSANUPDATE" == 1 ]; then - WEBGETSOURCE=$DEFAULTWEBUPDATE - else - WEBGETSOURCE=$DEFAULTWEBIMAGE - fi - fi - - if [ "$AUTOAPPLY" == 0 ]; then - # Need to check space on sdcard only if we dump the update there. - $ECHO "Checking free space on sdcard for the update download." - FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" - # we need about 50MB for the storage needs - if [ $FREEBLOCKS -le 50000 ]; then - $ECHO "Error: not enough free space available on sdcard for the update operation (need 50mb)" - $ECHO "Please free up space before invoking this option again." - $ECHO "Cleaning up, unmounting file systems, aborting." - umount /system /data /sdcard - exit 1 - fi - fi - - if [ ! `basename $WEBGETSOURCE` == `basename $WEBGETSOURCE .zip` ]; then - # It is a zip, not img. - ITSANUPDATE=1 - else - if [ ! `basename $WEBGETSOURCE` == `basename $WEBGETSOURCE .img` ]; then - # It is an img file. - ITSANIMAGE=1 - else - # Unknown file type - $ECHO "Unknown file type, cleaning up, aborting." - umount /system /data /sdcard - exit 1 - fi - fi - - - if [ "$ITSANUPDATE" == 1 -a "$AUTOAPPLY" == 0 ]; then - # Move the previous update aside, if things go badly with the new update, it is good - # to have the last one still around :-) - - # If we cannot figure out what the file name used to be, create this new one with a time stamp - OLDNAME="OLD-update-`date +%Y%m%d-%H%M`" - - if [ -e $WEBGETTARGET/update.zip ]; then - $ECHO "There is already an update.zip in $WEBGETTARGET, backing it up to" - if [ -e $WEBGETTARGET/update.name ]; then - OLDNAME=`cat $WEBGETTARGET/update.name` - # Backup the name file (presumably contains the old name of the update.zip - mv -f $WEBGETTARGET/update.name $WEBGETTARGET/`basename $OLDNAME .zip`.name - fi - $ECHO "`basename $OLDNAME .zip`.zip" - mv -f $WEBGETTARGET/update.zip $WEBGETTARGET/`basename $OLDNAME .zip`.zip - - # Backup the MD5sum file - if [ -e $WEBGETTARGET/update.MD5sum ]; then - mv -f $WEBGETTARGET/update.MD5sum $WEBGETTARGET/`basename $OLDNAME .zip`.MD5sum - fi - fi - fi - - $ECHO "Starting WiFI, please wait..." - insmod /system/lib/modules/wlan.ko - - wlan_loader -f /system/etc/wifi/Fw1251r1c.bin -e /proc/calibration -i /system/etc/wifi/tiwlan.ini - - CWD=`pwd` - cd /data/local/tmp - - wpa_supplicant -f -Dtiwlan0 -itiwlan0 -c/data/misc/wifi/wpa_supplicant.conf& - - sleep 5 - $ECHO "wpa_supplicant started" - $ECHO "" - - echo "nameserver $NAMESERVER1" >/etc/resolv.conf - echo "nameserver $NAMESERVER2" >>/etc/resolv.conf - - #We want the wifi to assign a dynamic address - $ECHO "Starting DHCPCD server (dynamic address assignment)" - # -BKL flags???? - dhcpcd -d tiwlan0 2>/dev/null & - - # Have to wait for it to init stuff - sleep 10 - - - CHECK1=`ps | grep -v grep | grep dhcpcd` - CHECK2=`ps | grep -v grep | grep wpa_supplicant` - if [ "$CHECK1" == "" -o "$CHECK2" == "" ]; then - $ECHO "Error: wpa_supplicant or DHCPCD server is not running, cleaning up, aborting" - rm -- -Dtiwlan0 - cd $CWD - - $ECHO "unmounting /system, /data and /sdcard" - umount /system - umount /data - umount /sdcard - exit 2 - fi - - $ECHO "DHCPCD server started" - $ECHO "" - - $ECHO "WiFi is running!" - $ECHO "" - - if [ "$AUTOAPPLY" == 1 ]; then - $ECHO "Autoapply is on, retrieving the update into /cache/`basename $WEBGETSOURCE`" - - wget -O /cache/`basename $WEBGETSOURCE` $WEBGETSOURCE $OUTPUT - - if [ ! -e /cache/recovery ]; then - mkdir /cache/recovery - chmod 777 /cache/recovery - fi - if [ -e /cache/recovery/command ]; then - echo "--update_package=CACHE:`basename $WEBGETSOURCE`" >>/cache/recovery/command - else - echo "--update_package=CACHE:`basename $WEBGETSOURCE`" >/cache/recovery/command - fi - chmod 555 /cache/recovery/command - # Once rebooted the update will be applied. - - else - - if [ "$ITSANUPDATE" == 1 ]; then - $ECHO "Retrieving system update into $WEBGETTARGET/update.zip, please wait..." - wget -O $WEBGETTARGET/update.zip $WEBGETSOURCE $OUTPUT - - echo "`basename $WEBGETSOURCE`" > $WEBGETTARGET/update.name - $ECHO "" - $ECHO "Update retrieved, if concerned, please compare the md5sum with the number" - $ECHO "you see on the web page, if it is NOT the same, the retrieval" - $ECHO "has failed and has to be repeated." - $ECHO "" - $ECHO `md5sum $WEBGETTARGET/update.zip | tee $WEBGETTARGET/update.MD5sum` - $ECHO "" - $ECHO "MD5sum has been stored in $WEBGETTARGET/update.MD5sum" - else - $ECHO "Retrieving the image into $WEBGETTARGET/`basename $WEBGETSOURCE`, please wait..." - wget -O $WEBGETTARGET/`basename $WEBGETSOURCE` $WEBGETSOURCE $OUTPUT - $ECHO "" - $ECHO "$WEBGETSOURCE retrieved, if concerned, please compare the md5sum with the number" - $ECHO "you see on the web page, if it is NOT the same, the retrieval" - $ECHO "has failed and has to be repeated." - $ECHO "" - md5sum $WEBGETTARGET/`basename $WEBGETSOURCE` | tee $WEBGETTARGET/`basename $WEBGETSOURCE .img`.MD5sum $OUTPUT - $ECHO "" - $ECHO "MD5sum has been stored in $WEBGETTARGET/`basename $WEBGETSOURCE .img`.MD5sum" - $ECHO "" - $ECHO -n "Would you like to flash this image into boot or recovery? (or no for no flash) " - read ANSWER - if [ "$ANSWER" == "boot" ]; then - $ECHO "Flashing $WEBGETTARGET/`basename $WEBGETSOURCE` into the boot partition." - $flash_image boot $WEBGETTARGET/`basename $WEBGETSOURCE` - else - if [ "$ANSWER" == "recovery" ]; then - $ECHO "Moving $WEBGETTARGET/`basename $WEBGETSOURCE` into the /data/recovery.img" - $ECHO "and /system/recovery.img" - cp -f $WEBGETTARGET/`basename $WEBGETSOURCE` /data/recovery.img - mount -o rw,remount /system - cp -f $WEBGETTARGET/`basename $WEBGETSOURCE` /system/recovery.img - $ECHO "Depending on the settings of your specific ROM, the recovery.img will be" - $ECHO "flashed at the normal bootup time either from /system or /data." - else - $ECHO "Not flashing the image." - fi - fi - fi - $ECHO "" - - fi - - $ECHO "Shutting down DHCPCD service and wpa_supplicant" - killall -TERM dhcpcd - TEMPVAR=`ps | grep -v grep | grep wpa_supplicant` - TEMPVAR=`echo $TEMPVAR | cut -f 1 -d ' '` - kill -TERM $TEMPVAR - - while true; do - CHECK=`ps | grep -v grep | grep dhcpcd` - if [ ! "$CHECK" == "" ]; then - sleep 1 - else - break - fi - done - - while true; do - CHECK=`ps | grep -v grep | grep wpa_supplicant` - if [ ! "$CHECK" == "" ]; then - sleep 1 - else - break - fi - done - #sleep 5 - - $ECHO "Cleaning up..." - # Looks like cannot clean up wlan module since chdir is missing - #rmmod wlan - rm -- -Dtiwlan0 - cd $CWD - - $ECHO "unmounting /system, /data and /sdcard" - umount /system - umount /data - umount /sdcard - - if [ "$AUTOAPPLY" == 1 ]; then - $ECHO "Auto apply update is on, rebooting into recovery to apply the update." - $ECHO "When the update is complete reboot into the normal mode." - $ECHO "The device will reboot and the update will be applied in 10 seconds!" - sleep 10 - reboot recovery - else - if [ "$ITSANUPDATE" == 1 ]; then - $ECHO "If you put the update into a folder other than /sdcard you need to use --getupdate to" - $ECHO "prepare the update for application." - $ECHO "You may want to execute 'reboot recovery' and choose update option to flash the update." - $ECHO "Or in the alternative, shutdown your phone with reboot -p, and then press +" - $ECHO "to initiate a normal system update procedure, if you have stock SPL." - fi - exit 0 - fi -fi - -# -------------------------------------DELETION, COMPRESSION OF BACKUPS--------------------------------- -if [ "$COMPRESS" == 1 -o "$DELETE" == 1 ]; then - $ECHO "Unmounting /system and /data to be on the safe side, mounting /sdcard read-write." - umount /system 2>/dev/null - umount /data 2>/dev/null - umount /sdcard 2>/dev/null - - FAIL=0 - # Since we are in recovery, these file-system have to be mounted - $ECHO "Mounting /sdcard to look for backups." - mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=1 - - if [ "$FAIL" == 1 ]; then - $ECHO "Error mounting /sdcard read-write, cleaning up..."; umount /system /data /sdcard; exit 1 - fi - - $ECHO "The current size of /sdcard FAT32 filesystem is `du /sdcard | tail -1 | cut -f 1 -d '/'`Kb" - $ECHO "" - - # find the oldest backup, but show the user other options - $ECHO "Looking for the oldest backup to delete, newest to compress," - $ECHO "will display all choices!" - $ECHO "" - $ECHO "Here are the backups you have picked within this repository $BACKUPPATH:" - - if [ "$DELETE" == 1 ]; then - RESTOREPATH=`ls -td $BACKUPPATH/*$SUBNAME* 2>/dev/null | tail -1` - ls -td $BACKUPPATH/*$SUBNAME* 2>/dev/null $OUTPUT - else - RESTOREPATH=`ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | tail -1` - ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null $OUTPUT - fi - $ECHO " " - - if [ "$RESTOREPATH" = "" ]; then - $ECHO "Error: no backups found" - exit 2 - else - if [ "$DELETE" == 1 ]; then - $ECHO "Default backup to delete is the oldest: $RESTOREPATH" - $ECHO "" - $ECHO "Other candidates for deletion are: " - ls -td $BACKUPPATH/*$SUBNAME* 2>/dev/null | grep -v $RESTOREPATH $OUTPUT - fi - if [ "$COMPRESS" == 1 ]; then - $ECHO "Default backup to compress is the latest: $RESTOREPATH" - $ECHO "" - $ECHO "Other candidates for compression are: " - ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | grep -v $RESTOREPATH $OUTPUT - fi - - $ECHO "" - $ECHO "Using G1 keyboard, enter a unique name substring to change it and " - $ECHO -n "or just to accept: " - if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then - read SUBSTRING - else - $ECHO "Accepting default." - SUBSTRING="" - fi - - if [ ! "$SUBSTRING" == "" ]; then - RESTOREPATH=`ls -td $BACKUPPATH/*$SUBNAME* 2>/dev/null | grep $SUBSTRING | tail -1` - else - RESTOREPATH=`ls -td $BACKUPPATH/*$SUBNAME* 2>/dev/null | tail -1` - fi - if [ "$RESTOREPATH" = "" ]; then - $ECHO "Error: no matching backup found, aborting" - exit 2 - fi - fi - - if [ "$DELETE" == 1 ]; then - $ECHO "Deletion path: $RESTOREPATH" - $ECHO "" - $ECHO "WARNING: Deletion of a backup is an IRREVERSIBLE action!!!" - $ECHO -n "Are you absolutely sure? {yes | YES | Yes | no | NO | No}: " - if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then - read ANSWER - else - ANSWER=yes - $ECHO "Accepting default." - fi - $ECHO "" - if [ "$ANSWER" == "yes" -o "$ANSWER" == "YES" -o "$ANSWER" == "Yes" ]; then - rm -rf $RESTOREPATH - $ECHO "" - $ECHO "$RESTOREPATH has been permanently removed from your SDCARD." - $ECHO "Post deletion size of the /sdcard FAT32 filesystem is `du /sdcard | tail -1 | cut -f 1 -d '/'`Kb" - else - if [ "$ANSWER" == "no" -o "$ANSWER" == "NO" -o "$ANSWER" == "No" ]; then - $ECHO "The chosen backup will NOT be removed." - else - $ECHO "Invalid answer: assuming NO." - fi - fi - fi - - if [ "$COMPRESS" == 1 ]; then - - CWD=`pwd` - cd $RESTOREPATH - - if [ `ls *.bz2 2>/dev/null|wc -l` -ge 1 -o `ls *.gz 2>/dev/null|wc -l` -ge 1 ]; then - $ECHO "This backup is already compressed, cleaning up, aborting..." - cd $CWD - umount /sdcard 2>/dev/null - exit 0 - fi - - $ECHO "checking free space on sdcard for the compression operation." - FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" - # we need about 70MB for the intermediate storage needs - if [ $FREEBLOCKS -le 70000 ]; then - $ECHO "Error: not enough free space available on sdcard for compression operation (need 70mb)" - $ECHO "leaving this backup uncompressed." - else - # we are already in $DESTDIR, start compression from the smallest files - # to maximize space for the largest's compression, less likely to fail. - # To decompress reverse the order. - $ECHO "Pre compression size of the /sdcard FAT32 filesystem is `du /sdcard | tail -1 | cut -f 1 -d '/'`Kb" - $ECHO "" - $ECHO "Compressing the backup may take a bit of time, please wait..." - $DEFAULTCOMPRESSOR $DEFAULTLEVEL `ls -S -r *` - $ECHO "" - $ECHO "Post compression size of the /sdcard FAT32 filesystem is `du /sdcard | tail -1 | cut -f 1 -d '/'`Kb" - fi - fi - - $ECHO "Cleaning up." - cd $CWD - umount /sdcard 2>/dev/null - exit 0 - -fi - -if [ "$GETUPDATE" == 1 ]; then - $ECHO "Unmounting /system and /data to be on the safe side, mounting /sdcard read-write." - umount /system 2>/dev/null - umount /data 2>/dev/null - umount /sdcard 2>/dev/null - - FAIL=0 - # Since we are in recovery, these file-system have to be mounted - $ECHO "Mounting /sdcard to look for updates to flash." - mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=1 - - if [ "$FAIL" == 1 ]; then - $ECHO "Error mounting /sdcard read-write, cleaning up..."; umount /system /data /sdcard; exit 1 - fi - - $ECHO "The current size of /sdcard FAT32 filesystem is `du /sdcard | tail -1 | cut -f 1 -d '/'`Kb" - $ECHO "" - - # find all the files with update in them, but show the user other options - $ECHO "Looking for all *update*.zip candidate files to flash." - $ECHO "" - $ECHO "Here are the updates limited by the subname $SUBNAME found" - $ECHO "within the repository $DEFAULTUPDATEPATH:" - $ECHO "" - RESTOREPATH=`ls -trd $DEFAULTUPDATEPATH/*$SUBNAME*.zip 2>/dev/null | grep update | tail -1` - if [ "$RESTOREPATH" == "" ]; then - $ECHO "Error: found no matching updates, cleaning up, aborting..." - umount /sdcard 2>/dev/null - exit 2 - fi - ls -trd $DEFAULTUPDATEPATH/*$SUBNAME*.zip 2>/dev/null | grep update $OUTPUT - $ECHO "" - $ECHO "The default update is the latest $RESTOREPATH" - $ECHO "" - $ECHO "Using G1 keyboard, enter a unique name substring to change it and " - $ECHO -n "or just to accept: " - if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then - read SUBSTRING - else - $ECHO "Accepting default." - SUBSTRING="" - fi - $ECHO "" - - if [ ! "$SUBSTRING" == "" ]; then - RESTOREPATH=`ls -trd $DEFAULTUPDATEPATH/*$SUBNAME*.zip 2>/dev/null | grep update | grep $SUBSTRING | tail -1` - else - RESTOREPATH=`ls -trd $DEFAULTUPDATEPATH/*$SUBNAME*.zip 2>/dev/null | grep update | tail -1` - fi - if [ "$RESTOREPATH" = "" ]; then - $ECHO "Error: no matching backups found, aborting" - exit 2 - fi - - if [ "$RESTOREPATH" == "/sdcard/update.zip" ]; then - $ECHO "You chose update.zip, it is ready for flashing, there nothing to do." - else - - # Things seem ok so far. - - # Move the previous update aside, if things go badly with the new update, it is good - # have the last one still around :-) - - # If we cannot figure out what the file name used to be, create this new one with a time stamp - OLDNAME="OLD-update-`date +%Y%m%d-%H%M`" - - if [ -e /sdcard/update.zip ]; then - $ECHO "There is already an update.zip in /sdcard, backing it up to" - if [ -e /sdcard/update.name ]; then - OLDNAME=`cat /sdcard/update.name` - # Backup the name file (presumably contains the old name of the update.zip - mv -f /sdcard/update.name /sdcard/`basename $OLDNAME .zip`.name - fi - $ECHO "`basename $OLDNAME .zip`.zip" - mv -f /sdcard/update.zip /sdcard/`basename $OLDNAME .zip`.zip - - # Backup the MD5sum file - if [ -e /sdcard/update.MD5sum ]; then - mv -f /sdcard/update.MD5sum /sdcard/`basename $OLDNAME .zip`.MD5sum - fi - fi - - if [ -e $DEFAULTUPDATEPATH/`basename $RESTOREPATH .zip`.MD5sum ]; then - mv -f $DEFAULTUPDATEPATH/`basename $RESTOREPATH .zip`.MD5sum /sdcard/update.MD5sum - else - $ECHO `md5sum $RESTOREPATH | tee /sdcard/update.MD5sum` - $ECHO "" - $ECHO "MD5sum has been stored in /sdcard/update.MD5sum" - $ECHO "" - fi - if [ -e $DEFAULTUPDATEPATH/`basename $RESTOREPATH .zip`.name ]; then - mv -f $DEFAULTUPDATEPATH/`basename $RESTOREPATH .zip`.name /sdcard/update.name - else - echo "`basename $RESTOREPATH`" > /sdcard/update.name - fi - - mv -i $RESTOREPATH /sdcard/update.zip - - - $ECHO "Your file $RESTOREPATH has been moved to the root of sdcard, and is ready for flashing!!!" - - fi - - $ECHO "You may want to execute 'reboot recovery' and then choose the update option to flash the update." - $ECHO "Or in the alternative, shutdown your phone with reboot -p, and then press + to" - $ECHO "initiate a standard update procedure if you have stock SPL." - $ECHO "" - $ECHO "Cleaning up and exiting." - umount /sdcard 2>/dev/null - exit 0 -fi +echo "Backup successful." From f9476fbfe8252ae111cc6792819d1e58f7a7a62a Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Sun, 21 Feb 2010 01:02:30 -0800 Subject: [PATCH 014/233] fix build break --- mtdutils/Android.mk | 4 ++-- nandroid/Android.mk | 14 ++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk index ff4ab3f..09d0fb8 100644 --- a/mtdutils/Android.mk +++ b/mtdutils/Android.mk @@ -28,7 +28,7 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SRC_FILES := flash_image.c -LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLE +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE := recovery_flash_image LOCAL_MODULE_TAGS := eng LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc @@ -40,7 +40,7 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SRC_FILES := dump_image.c mtdutils.c mounts.c -LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLE +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE := recovery_dump_image LOCAL_MODULE_TAGS := eng LOCAL_STATIC_LIBRARIES := libcutils libc diff --git a/nandroid/Android.mk b/nandroid/Android.mk index 79acc32..9423c09 100644 --- a/nandroid/Android.mk +++ b/nandroid/Android.mk @@ -1,10 +1,8 @@ -ifneq ($(TARGET_SIMULATOR),true) -ifeq ($(TARGET_ARCH),arm) - LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := recovery_nandroid -LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLE +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin LOCAL_SRC_FILES := nandroid-mobile.sh LOCAL_MODULE_STEM := nandroid-mobile.sh @@ -14,19 +12,19 @@ include $(BUILD_PREBUILT) include $(CLEAR_VARS) LOCAL_MODULE := recovery_unyaffs LOCAL_MODULE_STEM := unyaffs -LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLE +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_TAGS := eng LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_SRC_FILES := unyaffs.c LOCAL_STATIC_LIBRARIES := libc libcutils LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin -LOCAL_MODULE_SUBDIR := recovery ADDITIONAL_RECOVERY_EXECUTABLES += recovery_unyaffs include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_MODULE := unyaffs LOCAL_SRC_FILES := unyaffs.c +LOCAL_MODULE_TAGS := eng include $(BUILD_EXECUTABLE) -endif # TARGET_ARCH == arm -endif # !TARGET_SIMULATOR +include $(CLEAR_VARS) From 7e5a6610647f93ee6e982b528099fda4b42edb45 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Sun, 21 Feb 2010 01:09:52 -0800 Subject: [PATCH 015/233] move unyaffs into external/yaffs2 --- nandroid/Android.mk | 18 ------ nandroid/unyaffs.c | 118 ------------------------------------ nandroid/unyaffs.h | 144 -------------------------------------------- 3 files changed, 280 deletions(-) delete mode 100644 nandroid/unyaffs.c delete mode 100644 nandroid/unyaffs.h diff --git a/nandroid/Android.mk b/nandroid/Android.mk index 9423c09..993daba 100644 --- a/nandroid/Android.mk +++ b/nandroid/Android.mk @@ -10,21 +10,3 @@ ADDITIONAL_RECOVERY_EXECUTABLES += recovery_nandroid include $(BUILD_PREBUILT) include $(CLEAR_VARS) -LOCAL_MODULE := recovery_unyaffs -LOCAL_MODULE_STEM := unyaffs -LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE_TAGS := eng -LOCAL_FORCE_STATIC_EXECUTABLE := true -LOCAL_SRC_FILES := unyaffs.c -LOCAL_STATIC_LIBRARIES := libc libcutils -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin -ADDITIONAL_RECOVERY_EXECUTABLES += recovery_unyaffs -include $(BUILD_EXECUTABLE) - -include $(CLEAR_VARS) -LOCAL_MODULE := unyaffs -LOCAL_SRC_FILES := unyaffs.c -LOCAL_MODULE_TAGS := eng -include $(BUILD_EXECUTABLE) - -include $(CLEAR_VARS) diff --git a/nandroid/unyaffs.c b/nandroid/unyaffs.c deleted file mode 100644 index aa8a735..0000000 --- a/nandroid/unyaffs.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * unyaffs: extract files from yaffs2 file system image to current directory - * - * Created by Kai Wei - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "unyaffs.h" - -#define CHUNK_SIZE 2048 -#define SPARE_SIZE 64 -#define MAX_OBJECTS 10000 -#define YAFFS_OBJECTID_ROOT 1 - - -unsigned char data[CHUNK_SIZE + SPARE_SIZE]; -unsigned char *chunk_data = data; -unsigned char *spare_data = data + CHUNK_SIZE; -int img_file; - -char *obj_list[MAX_OBJECTS]; -void process_chunk() -{ - int out_file, remain, s; - char *full_path_name; - - yaffs_PackedTags2 *pt = (yaffs_PackedTags2 *)spare_data; - if (pt->t.byteCount == 0xffff) { //a new object - - yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)malloc(sizeof(yaffs_ObjectHeader)); - memcpy(oh, chunk_data, sizeof(yaffs_ObjectHeader)); - - full_path_name = (char *)malloc(strlen(oh->name) + strlen(obj_list[oh->parentObjectId]) + 2); - if (full_path_name == NULL) { - perror("malloc full path name\n"); - } - strcpy(full_path_name, obj_list[oh->parentObjectId]); - strcat(full_path_name, "/"); - strcat(full_path_name, oh->name); - obj_list[pt->t.objectId] = full_path_name; - - switch(oh->type) { - case YAFFS_OBJECT_TYPE_FILE: - remain = oh->fileSize; - out_file = creat(full_path_name, oh->yst_mode); - while(remain > 0) { - if (read_chunk()) - return -1; - s = (remain < pt->t.byteCount) ? remain : pt->t.byteCount; - if (write(out_file, chunk_data, s) == -1) - return -1; - remain -= s; - } - close(out_file); - break; - case YAFFS_OBJECT_TYPE_SYMLINK: - symlink(oh->alias, full_path_name); - break; - case YAFFS_OBJECT_TYPE_DIRECTORY: - mkdir(full_path_name, 0777); - break; - case YAFFS_OBJECT_TYPE_HARDLINK: - link(obj_list[oh->equivalentObjectId], full_path_name); - break; - } - } -} - - -int read_chunk() -{ - ssize_t s; - int ret = -1; - memset(chunk_data, 0xff, sizeof(chunk_data)); - s = read(img_file, data, CHUNK_SIZE + SPARE_SIZE); - if (s == -1) { - perror("read image file\n"); - } else if (s == 0) { - printf("end of image\n"); - } else if ((s == (CHUNK_SIZE + SPARE_SIZE))) { - ret = 0; - } else { - fprintf(stderr, "broken image file\n"); - } - return ret; -} - -int main(int argc, char **argv) -{ - if (argc != 2) { - printf("Usage: unyaffs image_file_name\n"); - exit(1); - } - img_file = open(argv[1], O_RDONLY); - if (img_file == -1) { - printf("open image file failed\n"); - exit(1); - } - - obj_list[YAFFS_OBJECTID_ROOT] = "."; - while(1) { - if (read_chunk() == -1) - break; - process_chunk(); - } - close(img_file); - return 0; -} diff --git a/nandroid/unyaffs.h b/nandroid/unyaffs.h deleted file mode 100644 index fcd8046..0000000 --- a/nandroid/unyaffs.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * definition copied from yaffs project - */ - -#ifndef __UNYAFFS_H__ -#define __UNYAFFS_H__ - - -#define YAFFS_MAX_NAME_LENGTH 255 -#define YAFFS_MAX_ALIAS_LENGTH 159 - -#include - -/* Definition of types */ -#ifndef __ASM_ARM_TYPES_H -typedef unsigned char __u8; -typedef unsigned short __u16; -typedef unsigned __u32; -#endif -typedef struct { - unsigned sequenceNumber; - unsigned objectId; - unsigned chunkId; - unsigned byteCount; -} yaffs_PackedTags2TagsPart; - -typedef struct { - unsigned char colParity; - unsigned lineParity; - unsigned lineParityPrime; -} yaffs_ECCOther; - -typedef struct { - yaffs_PackedTags2TagsPart t; - yaffs_ECCOther ecc; -} yaffs_PackedTags2; - -typedef enum { - YAFFS_ECC_RESULT_UNKNOWN, - YAFFS_ECC_RESULT_NO_ERROR, - YAFFS_ECC_RESULT_FIXED, - YAFFS_ECC_RESULT_UNFIXED -} yaffs_ECCResult; - -typedef enum { - YAFFS_OBJECT_TYPE_UNKNOWN, - YAFFS_OBJECT_TYPE_FILE, - YAFFS_OBJECT_TYPE_SYMLINK, - YAFFS_OBJECT_TYPE_DIRECTORY, - YAFFS_OBJECT_TYPE_HARDLINK, - YAFFS_OBJECT_TYPE_SPECIAL -} yaffs_ObjectType; - - -typedef struct { - - unsigned validMarker0; - unsigned chunkUsed; /* Status of the chunk: used or unused */ - unsigned objectId; /* If 0 then this is not part of an object (unused) */ - unsigned chunkId; /* If 0 then this is a header, else a data chunk */ - unsigned byteCount; /* Only valid for data chunks */ - - /* The following stuff only has meaning when we read */ - yaffs_ECCResult eccResult; - unsigned blockBad; - - /* YAFFS 1 stuff */ - unsigned chunkDeleted; /* The chunk is marked deleted */ - unsigned serialNumber; /* Yaffs1 2-bit serial number */ - - /* YAFFS2 stuff */ - unsigned sequenceNumber; /* The sequence number of this block */ - - /* Extra info if this is an object header (YAFFS2 only) */ - - unsigned extraHeaderInfoAvailable; /* There is extra info available if this is not zero */ - unsigned extraParentObjectId; /* The parent object */ - unsigned extraIsShrinkHeader; /* Is it a shrink header? */ - unsigned extraShadows; /* Does this shadow another object? */ - - yaffs_ObjectType extraObjectType; /* What object type? */ - - unsigned extraFileLength; /* Length if it is a file */ - unsigned extraEquivalentObjectId; /* Equivalent object Id if it is a hard link */ - - unsigned validMarker1; - -} yaffs_ExtendedTags; - -/* -------------------------- Object structure -------------------------------*/ -/* This is the object structure as stored on NAND */ - -typedef struct { - yaffs_ObjectType type; - - /* Apply to everything */ - int parentObjectId; - __u16 sum__NoLongerUsed; /* checksum of name. No longer used */ - char name[YAFFS_MAX_NAME_LENGTH + 1]; - - /* The following apply to directories, files, symlinks - not hard links */ - __u32 yst_mode; /* protection */ - -#ifdef CONFIG_YAFFS_WINCE - __u32 notForWinCE[5]; -#else - __u32 yst_uid; - __u32 yst_gid; - __u32 yst_atime; - __u32 yst_mtime; - __u32 yst_ctime; -#endif - - /* File size applies to files only */ - int fileSize; - - /* Equivalent object id applies to hard links only. */ - int equivalentObjectId; - - /* Alias is for symlinks only. */ - char alias[YAFFS_MAX_ALIAS_LENGTH + 1]; - - __u32 yst_rdev; /* device stuff for block and char devices (major/min) */ - -#ifdef CONFIG_YAFFS_WINCE - __u32 win_ctime[2]; - __u32 win_atime[2]; - __u32 win_mtime[2]; -#else - __u32 roomToGrow[6]; - -#endif - __u32 inbandShadowsObject; - __u32 inbandIsShrink; - - __u32 reservedSpace[2]; - int shadowsObject; /* This object header shadows the specified object if > 0 */ - - /* isShrink applies to object headers written when we shrink the file (ie resize) */ - __u32 isShrink; - -} yaffs_ObjectHeader; - -#endif From 1a7ee5384de65fcb642f6c04616d822f06bd06da Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Sun, 21 Feb 2010 17:52:30 -0800 Subject: [PATCH 016/233] add mkfstab.sh script to create an fstab on any device. clean up dump_image to use libmtdutils --- Android.mk | 2 +- legacy.c | 9 --------- mtdutils/Android.mk | 8 +++++--- nandroid/Android.mk | 10 ++++++++++ nandroid/nandroid-mobile.sh | 2 +- 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/Android.mk b/Android.mk index 2afb46f..688acb9 100644 --- a/Android.mk +++ b/Android.mk @@ -41,10 +41,10 @@ ifeq ($(TARGET_RECOVERY_UI_LIB),) else LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB) endif +LOCAL_STATIC_LIBRARIES += libamend LOCAL_STATIC_LIBRARIES += libminzip libunz libmtdutils libmincrypt LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils LOCAL_STATIC_LIBRARIES += libstdc++ libc -LOCAL_STATIC_LIBRARIES += libamend include $(BUILD_EXECUTABLE) diff --git a/legacy.c b/legacy.c index cf903ce..88c620e 100644 --- a/legacy.c +++ b/legacy.c @@ -75,15 +75,6 @@ static int read_data(ZipArchive *zip, const ZipEntry *entry, int handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) { - // This is bizarre. The build fails with "undefined reference" - // unless the following two functions are referenced from somewhere to - // force them to be linked. This seems to be a problem with yacc/lex. - if (zip == 1) - { - fwrite(NULL, 0, 0, NULL); - fileno(NULL); - } - /* Read the entire script into a buffer. */ int script_len; diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk index 09d0fb8..e0cc524 100644 --- a/mtdutils/Android.mk +++ b/mtdutils/Android.mk @@ -21,9 +21,11 @@ LOCAL_SHARED_LIBRARIES := libcutils libc include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) -LOCAL_SRC_FILES := dump_image.c mtdutils.c mounts.c +LOCAL_SRC_FILES := dump_image.c LOCAL_MODULE := dump_image LOCAL_MODULE_TAGS := eng +LOCAL_STATIC_LIBRARIES := libmtdutils +LOCAL_SHARED_LIBRARIES := libcutils libc include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) @@ -39,11 +41,11 @@ ADDITIONAL_RECOVERY_EXECUTABLES += recovery_flash_image include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) -LOCAL_SRC_FILES := dump_image.c mtdutils.c mounts.c +LOCAL_SRC_FILES := dump_image.c LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE := recovery_dump_image LOCAL_MODULE_TAGS := eng -LOCAL_STATIC_LIBRARIES := libcutils libc +LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc LOCAL_MODULE_STEM := dump_image LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin diff --git a/nandroid/Android.mk b/nandroid/Android.mk index 993daba..4b22437 100644 --- a/nandroid/Android.mk +++ b/nandroid/Android.mk @@ -10,3 +10,13 @@ ADDITIONAL_RECOVERY_EXECUTABLES += recovery_nandroid include $(BUILD_PREBUILT) include $(CLEAR_VARS) +LOCAL_MODULE := recovery_mkfstab +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := mkfstab.sh +LOCAL_MODULE_STEM := mkfstab.sh +ADDITIONAL_RECOVERY_EXECUTABLES += recovery_mkfstab +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) diff --git a/nandroid/nandroid-mobile.sh b/nandroid/nandroid-mobile.sh index 9a9a824..4929074 100755 --- a/nandroid/nandroid-mobile.sh +++ b/nandroid/nandroid-mobile.sh @@ -56,7 +56,7 @@ DEVICEID=foo RECOVERY=foo echo "nandroid-mobile v2.1" - +mkfstab.sh > /etc/fstab if [ "$1" == "" ]; then echo "Usage: $0 {backup|restore} [/path/to/nandroid/backup/]" From 466e67a582bfe9bd8b025ba7e215ae44879a5ca7 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Sun, 21 Feb 2010 19:29:32 -0800 Subject: [PATCH 017/233] generate fstab using mkfstab.sh. implement restore of system data and cache in nandroid --- nandroid/mkfstab.sh | 33 +++++++++++++++++++++++++++++++ nandroid/nandroid-mobile.sh | 39 ++++++++++++++++++++++++------------- 2 files changed, 59 insertions(+), 13 deletions(-) create mode 100755 nandroid/mkfstab.sh diff --git a/nandroid/mkfstab.sh b/nandroid/mkfstab.sh new file mode 100755 index 0000000..649ae2f --- /dev/null +++ b/nandroid/mkfstab.sh @@ -0,0 +1,33 @@ +#!/sbin/sh + +cat /proc/mtd | while read mtdentry +do + mtd=$(echo $mtdentry | awk '{print $1}') + mtd=$(echo $mtd | sed s/mtd//) + mtd=$(echo $mtd | sed s/://) + exist=$(ls -l /dev/block/mtdblock$mtd) 2> /dev/null + if [ -z "$exist" ] + then + continue + fi + partition=$(echo $mtdentry | awk '{print $4}') + partition=$(echo $partition | sed s/\"//g) + mount=$partition + type= + if [ "$partition" = "system" ] + then + type=yaffs2 + elif [ "$partition" = "userdata" ] + then + type=yaffs2 + mount=data + elif [ "$partition" == "cache" ] + then + type=yaffs2 + else + continue + fi + + echo "/dev/block/mtdblock$mtd /$mount $type rw" +done +echo "/dev/block/mmcblk0p1" /sdcard vfat rw \ No newline at end of file diff --git a/nandroid/nandroid-mobile.sh b/nandroid/nandroid-mobile.sh index 4929074..e8ae697 100755 --- a/nandroid/nandroid-mobile.sh +++ b/nandroid/nandroid-mobile.sh @@ -94,6 +94,11 @@ case $1 in exit 1 fi fi + unyaffs=`which unyaffs` + if [ "$unyaffs" == "" ]; then + echo "error: unyaffs not found in path" + exit 1 + fi break ;; esac @@ -133,17 +138,15 @@ case $1 in echo "error: $RESTOREPATH/nandroid.md5 not found, cannot verify backup data" exit 1 fi - umount /system 2>/dev/null - umount /data 2>/dev/null - if [ ! "`mount | grep data`" == "" ]; then - echo "error: unable to umount /data, aborting" - exit 1 - fi - if [ ! "`mount | grep system`" == "" ]; then - echo "error: unable to umount /system, aborting" - exit 1 - fi - + umount /system 2>/dev/null + umount /data 2>/dev/null + mount -o rw /system || FAIL=1 + mount -o rw /data || FAIL=2 + case $FAIL in + 1) echo "Error mounting system read-write"; umount /system /data /cache; exit 1;; + 2) echo "Error mounting data read-write"; umount /system /data /cache; exit 1;; + esac + echo "Verifying backup images..." CWD=$PWD cd $RESTOREPATH @@ -156,8 +159,18 @@ case $1 in echo "Flashing $image..." $flash_image $image $image.img done - echo "Flashing system and data not currently supported" - echo "Restore done" + curdir=$(pwd) + for image in system data cache; do + echo "Unpacking $image..." + cd /$image + rm -rf * + unyaffs $curdir/$image.img + cd $curdir + done + sync + umount /system + umount /data + echo "Restore done" exit 0 ;; backup) From bcdd00359cf84e47aeae24949b465ceaf380c9ff Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Sun, 21 Feb 2010 21:10:25 -0800 Subject: [PATCH 018/233] Functional Nandroid --- default_recovery_ui.c | 4 +- extendedcommands.c | 246 +++++++++++++++++++++++++++++------------- extendedcommands.h | 8 +- recovery.c | 14 +-- recovery_ui.h | 4 +- 5 files changed, 191 insertions(+), 85 deletions(-) diff --git a/default_recovery_ui.c b/default_recovery_ui.c index 2c39a78..76c39af 100644 --- a/default_recovery_ui.c +++ b/default_recovery_ui.c @@ -29,8 +29,8 @@ char* MENU_ITEMS[] = { "reboot system now", "wipe data/factory reset", "wipe cache partition", "install zip from sdcard", - "toggle signature verification", - "toggle script asserts", + "backup", + "restore", NULL }; int device_toggle_display(volatile char* key_pressed, int key_code) { diff --git a/extendedcommands.c b/extendedcommands.c index 4ef23f7..c491d84 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -28,6 +28,7 @@ int signature_check_enabled = 1; int script_assert_enabled = 1; +static const char *SDCARD_PACKAGE_FILE = "SDCARD:update.zip"; void toggle_signature_check() @@ -42,12 +43,68 @@ void toggle_script_asserts() ui_print("Script Asserts: %s\n", script_assert_enabled ? "Enabled" : "Disabled"); } -void show_choose_zip_menu() +void install_zip(const char* packagefilepath) +{ + ui_print("\n-- Installing: %s\n", packagefilepath); + set_sdcard_update_bootloader_message(); + int status = install_package(packagefilepath); + if (status != INSTALL_SUCCESS) { + ui_set_background(BACKGROUND_ICON_ERROR); + ui_print("Installation aborted.\n"); + } else if (!ui_text_visible()) { + return; // reboot if logs aren't visible + } else { + if (firmware_update_pending()) { + ui_print("\nReboot via menu to complete\n" + "installation.\n"); + } else { + ui_print("\nInstall from sdcard complete.\n"); + } + } +} + +char* INSTALL_MENU_ITEMS[] = { "apply sdcard:update.zip", + "choose zip from sdcard", + "toggle signature verification", + "toggle script asserts", + NULL }; +#define ITEM_APPLY_SDCARD 0 +#define ITEM_CHOOSE_ZIP 1 +#define ITEM_SIG_CHECK 2 +#define ITEM_ASSERTS 3 + +void show_install_update_menu() +{ + static char* headers[] = { "Apply update from .zip file on SD card", + "", + NULL + }; + for (;;) + { + int chosen_item = get_menu_selection(headers, INSTALL_MENU_ITEMS, 0); + switch (chosen_item) + { + case ITEM_ASSERTS: + toggle_script_asserts(); + break; + case ITEM_SIG_CHECK: + toggle_signature_check(); + break; + case ITEM_APPLY_SDCARD: + install_zip(SDCARD_PACKAGE_FILE); + break; + case ITEM_CHOOSE_ZIP: + show_choose_zip_menu(); + break; + default: + return; + } + + } +} + +char* choose_file_menu(const char* directory, const char* extension, const char* headers[]) { - static char* headers[] = { "Choose a zip or press POWER to return", - "", - NULL }; - char path[PATH_MAX] = ""; DIR *dir; struct dirent *de; @@ -56,83 +113,126 @@ void show_choose_zip_menu() char** files; char** list; + dir = opendir(directory); + if (dir == NULL) { + ui_print("Couldn't open directory.\n"); + return NULL; + } + + const int extension_length = strlen(extension); + + while ((de=readdir(dir)) != NULL) { + if (de->d_name[0] != '.' && strlen(de->d_name) > extension_length && strcmp(de->d_name + strlen(de->d_name) - extension_length, extension) == 0) { + total++; + } + } + + if (total==0) { + ui_print("No files found.\n"); + if(closedir(dir) < 0) { + LOGE("Failed to close directory."); + } + return NULL; + } + + files = (char**) malloc((total+1)*sizeof(char*)); + files[total]=NULL; + + list = (char**) malloc((total+1)*sizeof(char*)); + list[total]=NULL; + + rewinddir(dir); + + i = 0; + while ((de = readdir(dir)) != NULL) { + if (de->d_name[0] != '.' && strlen(de->d_name) > extension_length && strcmp(de->d_name + strlen(de->d_name) - extension_length, extension) == 0) { + files[i] = (char*) malloc(strlen(directory)+strlen(de->d_name)+1); + strcpy(files[i], directory); + strcat(files[i], de->d_name); + + list[i] = (char*) malloc(strlen(de->d_name)+1); + strcpy(list[i], de->d_name); + + i++; + } + } + + if (closedir(dir) <0) { + LOGE("Failure closing directory."); + return NULL; + } + + int chosen_item = get_menu_selection(headers, list, 1); + static char ret[PATH_MAX]; + strcpy(ret, files[chosen_item]); + return ret; +} + +void show_choose_zip_menu() +{ if (ensure_root_path_mounted("SDCARD:") != 0) { LOGE ("Can't mount /sdcard\n"); return; } - dir = opendir("/sdcard"); - if (dir == NULL) { - LOGE("Couldn't open /sdcard"); + static char* headers[] = { "Choose a zip to apply", + "", + NULL + }; + + char* file = choose_file_menu("/sdcard/", ".zip", headers); + if (file == NULL) + return; + char sdcard_package_file[1024]; + strcpy(sdcard_package_file, "SDCARD:"); + strcat(sdcard_package_file, file + strlen("/sdcard/")); + install_zip(sdcard_package_file); +} + +void do_nandroid_backup() +{ + ui_print("Performing backup...\n"); + system("/sbin/nandroid-mobile.sh backup"); + ui_print("Backup complete.\n"); +} + +void show_nandroid_restore_menu() +{ + if (ensure_root_path_mounted("SDCARD:") != 0) { + LOGE ("Can't mount /sdcard\n"); return; } + + static char* headers[] = { "Choose an image to restore", + "", + NULL + }; - const char *extension = ".zip"; - const int extension_length = strlen(extension); - - while ((de=readdir(dir)) != NULL) { - if (de->d_name[0] != '.' && strlen(de->d_name) > extension_length && strcmp(de->d_name + strlen(de->d_name) - extension_length, extension) == 0) { - total++; - } + system("cat /proc/cmdline | sed 's/.*serialno=//' | cut -d' ' -f1 > /.deviceid"); + FILE *deviceIdFile = fopen(".deviceid", "r"); + char deviceId[256]; + if (deviceIdFile == NULL) + { + ui_print("Unable to retrieve device id.\n"); + return; } - - if (total==0) { - LOGE("No tar archives found\n"); - if(closedir(dir) < 0) { - LOGE("Failed to close directory /sdcard"); - return; - } + int readCount = fread(deviceId, 1, sizeof(deviceId), deviceIdFile); + if (readCount == 0) + { + ui_print("Unable to retrieve device id.\n"); + return; } - else { - files = (char**) malloc((total+1)*sizeof(char*)); - files[total]=NULL; + deviceId[readCount - 1] = NULL; + fclose(deviceIdFile); + char backupDirectory[PATH_MAX]; + sprintf(backupDirectory, "/sdcard/nandroid/%s/", deviceId); - list = (char**) malloc((total+1)*sizeof(char*)); - list[total]=NULL; - - rewinddir(dir); - - i = 0; - while ((de = readdir(dir)) != NULL) { - if (de->d_name[0] != '.' && strlen(de->d_name) > extension_length && strcmp(de->d_name + strlen(de->d_name) - extension_length, extension) == 0) { - files[i] = (char*) malloc(strlen("/sdcard/")+strlen(de->d_name)+1); - strcpy(files[i], "/sdcard/"); - strcat(files[i], de->d_name); - - list[i] = (char*) malloc(strlen(de->d_name)+1); - strcpy(list[i], de->d_name); - - i++; - } - } - - if (closedir(dir) <0) { - LOGE("Failure closing directory /sdcard\n"); - return; - } - - int chosen_item = get_menu_selection(headers, list, 1); - if (chosen_item >= 0 && chosen_item != GO_BACK) { - char sdcard_package_file[1024]; - strcpy(sdcard_package_file, "SDCARD:"); - strcat(sdcard_package_file, files[chosen_item] + strlen("/sdcard")); - - ui_print("\n-- Install from sdcard...\n"); - set_sdcard_update_bootloader_message(); - int status = install_package(sdcard_package_file); - if (status != INSTALL_SUCCESS) { - ui_set_background(BACKGROUND_ICON_ERROR); - ui_print("Installation aborted.\n"); - } else if (!ui_text_visible()) { - return; // reboot if logs aren't visible - } else { - if (firmware_update_pending()) { - ui_print("\nReboot via menu to complete\n" - "installation.\n"); - } else { - ui_print("\nInstall from sdcard complete.\n"); - } - } - } - } + char* file = choose_file_menu(backupDirectory, "", headers); + if (file == NULL) + return; + char* command[PATH_MAX]; + sprintf(command, "nandroid-mobile.sh restore %s", file); + ui_print("Performing restore...\n"); + system(command); + ui_print("Restore complete.\n"); } \ No newline at end of file diff --git a/extendedcommands.h b/extendedcommands.h index 2729de2..00dff0f 100644 --- a/extendedcommands.h +++ b/extendedcommands.h @@ -14,4 +14,10 @@ int get_allow_toggle_display(); int -ui_get_show_menu(); \ No newline at end of file +ui_get_show_menu(); + +void +do_nandroid_backup(); + +void +show_nandroid_restore_menu(); \ No newline at end of file diff --git a/recovery.c b/recovery.c index e398393..a88c696 100644 --- a/recovery.c +++ b/recovery.c @@ -440,14 +440,14 @@ prompt_and_wait() } } break; - case ITEM_SIG_CHECK: - toggle_signature_check(); - break; - case ITEM_ASSERTS: - toggle_script_asserts(); - break; case ITEM_INSTALL_ZIP: - show_choose_zip_menu(); + show_install_update_menu(); + break; + case ITEM_BACKUP: + do_nandroid_backup(); + break; + case ITEM_RESTORE: + show_nandroid_restore_menu(); break; } } diff --git a/recovery_ui.h b/recovery_ui.h index 4d3d3e2..2964dfc 100644 --- a/recovery_ui.h +++ b/recovery_ui.h @@ -68,8 +68,8 @@ int device_wipe_data(); #define ITEM_WIPE_DATA 2 #define ITEM_WIPE_CACHE 3 #define ITEM_INSTALL_ZIP 4 -#define ITEM_SIG_CHECK 5 -#define ITEM_ASSERTS 6 +#define ITEM_BACKUP 5 +#define ITEM_RESTORE 6 // Header text to display above the main menu. extern char* MENU_HEADERS[]; From 1fa52ecdbf069247e699e2f48f5d21ea0506cc0a Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Sun, 21 Feb 2010 21:29:10 -0800 Subject: [PATCH 019/233] add mount sdcard option --- default_recovery_ui.c | 1 + recovery.c | 5 +++++ recovery_ui.h | 1 + 3 files changed, 7 insertions(+) diff --git a/default_recovery_ui.c b/default_recovery_ui.c index 76c39af..d832794 100644 --- a/default_recovery_ui.c +++ b/default_recovery_ui.c @@ -31,6 +31,7 @@ char* MENU_ITEMS[] = { "reboot system now", "install zip from sdcard", "backup", "restore", + "mount sdcard", NULL }; int device_toggle_display(volatile char* key_pressed, int key_code) { diff --git a/recovery.c b/recovery.c index a88c696..f7d61d2 100644 --- a/recovery.c +++ b/recovery.c @@ -449,6 +449,11 @@ prompt_and_wait() case ITEM_RESTORE: show_nandroid_restore_menu(); break; + case ITEM_MOUNT_SDCARD: + if (ensure_root_path_mounted("SDCARD:") != 0) { + LOGE ("Can't mount /sdcard\n"); + } + break; } } } diff --git a/recovery_ui.h b/recovery_ui.h index 2964dfc..7165007 100644 --- a/recovery_ui.h +++ b/recovery_ui.h @@ -70,6 +70,7 @@ int device_wipe_data(); #define ITEM_INSTALL_ZIP 4 #define ITEM_BACKUP 5 #define ITEM_RESTORE 6 +#define ITEM_MOUNT_SDCARD 7 // Header text to display above the main menu. extern char* MENU_HEADERS[]; From 225c6b4673ea880bc41a46becdc8531e90a9c90b Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Sun, 21 Feb 2010 22:02:24 -0800 Subject: [PATCH 020/233] do error checking of nandroid operations --- extendedcommands.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/extendedcommands.c b/extendedcommands.c index c491d84..a59b9b0 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -192,7 +192,11 @@ void show_choose_zip_menu() void do_nandroid_backup() { ui_print("Performing backup...\n"); - system("/sbin/nandroid-mobile.sh backup"); + if (system("/sbin/nandroid-mobile.sh backup") != 0) + { + ui_print("Error while backing up!\n"); + return; + } ui_print("Backup complete.\n"); } @@ -208,10 +212,10 @@ void show_nandroid_restore_menu() NULL }; - system("cat /proc/cmdline | sed 's/.*serialno=//' | cut -d' ' -f1 > /.deviceid"); + int ret = system("cat /proc/cmdline | sed 's/.*serialno=//' | cut -d' ' -f1 > /.deviceid"); FILE *deviceIdFile = fopen(".deviceid", "r"); char deviceId[256]; - if (deviceIdFile == NULL) + if (deviceIdFile == NULL || ret == 0) { ui_print("Unable to retrieve device id.\n"); return; @@ -233,6 +237,11 @@ void show_nandroid_restore_menu() char* command[PATH_MAX]; sprintf(command, "nandroid-mobile.sh restore %s", file); ui_print("Performing restore...\n"); - system(command); + ret = system(command); + if (ret != 0) + { + ui_print("Error while restoring!\n"); + return; + } ui_print("Restore complete.\n"); } \ No newline at end of file From 981b0cd1e14d8b53eb60f7af8a1b60e2d858c974 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Mon, 22 Feb 2010 08:53:34 -0800 Subject: [PATCH 021/233] roll recovery tools into a single binary. fix up nandroid to work without deviceid requirement --- extendedcommands.c | 37 ++++++++++++------------------------ mtdutils/Android.mk | 38 +++++++++++++++++++++++++------------ mtdutils/dump_image.c | 2 +- nandroid/nandroid-mobile.sh | 12 +++--------- recovery.c | 1 + 5 files changed, 43 insertions(+), 47 deletions(-) diff --git a/extendedcommands.c b/extendedcommands.c index a59b9b0..0269f64 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -162,10 +162,16 @@ char* choose_file_menu(const char* directory, const char* extension, const char* return NULL; } - int chosen_item = get_menu_selection(headers, list, 1); - static char ret[PATH_MAX]; - strcpy(ret, files[chosen_item]); - return ret; + for (;;) + { + int chosen_item = get_menu_selection(headers, list, 0); + if (chosen_item == GO_BACK) + break; + static char ret[PATH_MAX]; + strcpy(ret, files[chosen_item]); + return ret; + } + return NULL; } void show_choose_zip_menu() @@ -211,33 +217,14 @@ void show_nandroid_restore_menu() "", NULL }; - - int ret = system("cat /proc/cmdline | sed 's/.*serialno=//' | cut -d' ' -f1 > /.deviceid"); - FILE *deviceIdFile = fopen(".deviceid", "r"); - char deviceId[256]; - if (deviceIdFile == NULL || ret == 0) - { - ui_print("Unable to retrieve device id.\n"); - return; - } - int readCount = fread(deviceId, 1, sizeof(deviceId), deviceIdFile); - if (readCount == 0) - { - ui_print("Unable to retrieve device id.\n"); - return; - } - deviceId[readCount - 1] = NULL; - fclose(deviceIdFile); - char backupDirectory[PATH_MAX]; - sprintf(backupDirectory, "/sdcard/nandroid/%s/", deviceId); - char* file = choose_file_menu(backupDirectory, "", headers); + char* file = choose_file_menu("/sdcard/nandroid/", "", headers); if (file == NULL) return; char* command[PATH_MAX]; sprintf(command, "nandroid-mobile.sh restore %s", file); ui_print("Performing restore...\n"); - ret = system(command); + int ret = system(command); if (ret != 0) { ui_print("Error while restoring!\n"); diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk index e0cc524..c45a75d 100644 --- a/mtdutils/Android.mk +++ b/mtdutils/Android.mk @@ -30,26 +30,40 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SRC_FILES := flash_image.c -LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +#LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE := recovery_flash_image -LOCAL_MODULE_TAGS := eng -LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc -LOCAL_MODULE_STEM := flash_image -LOCAL_FORCE_STATIC_EXECUTABLE := true -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin -ADDITIONAL_RECOVERY_EXECUTABLES += recovery_flash_image -include $(BUILD_EXECUTABLE) +#LOCAL_MODULE_TAGS := eng +#LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc +#LOCAL_MODULE_STEM := flash_image +#LOCAL_FORCE_STATIC_EXECUTABLE := true +#LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_CFLAGS += -Dmain=flash_image_main +#ADDITIONAL_RECOVERY_EXECUTABLES += recovery_flash_image +include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := dump_image.c -LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +#LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE := recovery_dump_image +#LOCAL_MODULE_TAGS := eng +#LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc +#LOCAL_MODULE_STEM := dump_image +#LOCAL_FORCE_STATIC_EXECUTABLE := true +#LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_CFLAGS += -Dmain=dump_image_main +#ADDITIONAL_RECOVERY_EXECUTABLES += recovery_dump_image +include $(BUILD_STATIC_LIBRARY) + + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := driver.c +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE := recovery_tools LOCAL_MODULE_TAGS := eng -LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc -LOCAL_MODULE_STEM := dump_image +LOCAL_STATIC_LIBRARIES := recovery_mkyaffs2image recovery_unyaffs recovery_dump_image recovery_flash_image libmtdutils libcutils libc LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin -ADDITIONAL_RECOVERY_EXECUTABLES += recovery_dump_image +ADDITIONAL_RECOVERY_EXECUTABLES += recovery_tools include $(BUILD_EXECUTABLE) endif # TARGET_ARCH == arm diff --git a/mtdutils/dump_image.c b/mtdutils/dump_image.c index bc96abc..8c3a052 100644 --- a/mtdutils/dump_image.c +++ b/mtdutils/dump_image.c @@ -34,7 +34,7 @@ #define BLOCK_SIZE 2048 #define SPARE_SIZE (BLOCK_SIZE >> 5) -void die(const char *msg, ...) { +static void die(const char *msg, ...) { int err = errno; va_list args; va_start(args, msg); diff --git a/nandroid/nandroid-mobile.sh b/nandroid/nandroid-mobile.sh index e8ae697..ba4b85e 100755 --- a/nandroid/nandroid-mobile.sh +++ b/nandroid/nandroid-mobile.sh @@ -52,7 +52,6 @@ # 9. print success. -DEVICEID=foo RECOVERY=foo echo "nandroid-mobile v2.1" @@ -60,7 +59,7 @@ mkfstab.sh > /etc/fstab if [ "$1" == "" ]; then echo "Usage: $0 {backup|restore} [/path/to/nandroid/backup/]" - echo "- backup will store a full system backup on /sdcard/nandroid/$DEVICEID" + echo "- backup will store a full system backup on /sdcard/nandroid" echo "- restore path will restore the last made backup for boot, system, recovery and data" exit 0 fi @@ -104,16 +103,11 @@ case $1 in esac # 1 -DEVICEID=`cat /proc/cmdline | sed "s/.*serialno=//" | cut -d" " -f1` RECOVERY=`cat /proc/cmdline | grep "androidboot.mode=recovery"` if [ "$RECOVERY" == "foo" ]; then echo "error: not running in recovery mode, aborting" exit 1 fi -if [ "$DEVICEID" == "foo" ]; then - echo "error: device id not found in /proc/cmdline, aborting" - exit 1 -fi if [ ! "`id -u 2>/dev/null`" == "0" ]; then if [ "`whoami 2>&1 | grep 'uid 0'`" == "" ]; then echo "error: must run as root, aborting" @@ -178,7 +172,7 @@ case $1 in ;; *) echo "Usage: $0 {backup|restore} [/path/to/nandroid/backup/]" - echo "- backup will store a full system backup on /sdcard/nandroid/$DEVICEID" + echo "- backup will store a full system backup on /sdcard/nandroid" echo "- restore path will restore the last made backup for boot, system, recovery and data" exit 1 ;; @@ -199,7 +193,7 @@ case $FAIL in esac TIMESTAMP="`date +%Y%m%d-%H%M`" -DESTDIR="/sdcard/nandroid/$DEVICEID/$TIMESTAMP" +DESTDIR="/sdcard/nandroid/$TIMESTAMP" if [ ! -d $DESTDIR ]; then mkdir -p $DESTDIR if [ ! -d $DESTDIR ]; then diff --git a/recovery.c b/recovery.c index f7d61d2..45911ea 100644 --- a/recovery.c +++ b/recovery.c @@ -346,6 +346,7 @@ get_menu_selection(char** headers, char** items, int menu_only) { } ui_end_menu(); + ui_clear_key_queue(); return chosen_item; } From 7400dc2700fa11cff329508df9667a326a46c37c Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Mon, 22 Feb 2010 08:53:47 -0800 Subject: [PATCH 022/233] missing file --- mtdutils/driver.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 mtdutils/driver.c diff --git a/mtdutils/driver.c b/mtdutils/driver.c new file mode 100644 index 0000000..351181f --- /dev/null +++ b/mtdutils/driver.c @@ -0,0 +1,17 @@ +#undef main + +#include +#include + +int main(int argc, char** argv) +{ + if (strstr(argv[0], "flash_image") != NULL) + return flash_image_main(argc, argv); + if (strstr(argv[0], "dump_image") != NULL) + return dump_image_main(argc, argv); + if (strstr(argv[0], "mkyaffs2image") != NULL) + return mkyaffs2image_main(argc, argv); + if (strstr(argv[0], "unyaffs") != NULL) + return unyaffs_main(argc, argv); + return 0; +} \ No newline at end of file From 007439bef67853585ec03c44a0606f369d69b2e2 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Mon, 22 Feb 2010 19:01:54 -0800 Subject: [PATCH 023/233] never restore recovery --- nandroid/nandroid-mobile.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nandroid/nandroid-mobile.sh b/nandroid/nandroid-mobile.sh index ba4b85e..738299d 100755 --- a/nandroid/nandroid-mobile.sh +++ b/nandroid/nandroid-mobile.sh @@ -149,7 +149,7 @@ case $1 in echo "error: md5sum mismatch, aborting" exit 1 fi - for image in boot recovery; do + for image in boot; do echo "Flashing $image..." $flash_image $image $image.img done From 69b75410de021a8beec340c28a9d9f222cb6e53f Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Tue, 23 Feb 2010 18:07:31 -0800 Subject: [PATCH 024/233] do not check the results of a run_program if script asserts are disabled --- commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands.c b/commands.c index e6fb9be..a60366f 100644 --- a/commands.c +++ b/commands.c @@ -397,7 +397,7 @@ cmd_run_program(const char *name, void *cookie, int argc, const char *argv[], int status; waitpid(pid, &status, 0); - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + if (WIFEXITED(status) && WEXITSTATUS(status) == 0 || !script_assert_enabled) { return 0; } else { LOGE("Error in %s\n(Status %d)\n", path, status); From 5740b042b56f02d9b038f7637a3ab1255527c8a9 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Tue, 23 Feb 2010 22:41:36 -0800 Subject: [PATCH 025/233] change the nandroid backup directory to something recovery specific, to reo prevent possible collisions --- extendedcommands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extendedcommands.c b/extendedcommands.c index 0269f64..61b0252 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -218,7 +218,7 @@ void show_nandroid_restore_menu() NULL }; - char* file = choose_file_menu("/sdcard/nandroid/", "", headers); + char* file = choose_file_menu("/sdcard/clockworkmod/backup/", "", headers); if (file == NULL) return; char* command[PATH_MAX]; From a37e9b1f19ebc5fa6caa09bb6dd78ba2bad8d79b Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Wed, 24 Feb 2010 13:13:34 -0800 Subject: [PATCH 026/233] Patch from Magnus to fix issues with bad blocks and dumping a boot image. Need to clean up the patch a bit. Also reverted mtdutils.s to korg/eclair-release --- mtdutils/dump_image.c | 14 ++++++-- mtdutils/mtdutils.c | 82 ------------------------------------------- 2 files changed, 12 insertions(+), 84 deletions(-) diff --git a/mtdutils/dump_image.c b/mtdutils/dump_image.c index 8c3a052..ddaf6f6 100644 --- a/mtdutils/dump_image.c +++ b/mtdutils/dump_image.c @@ -97,7 +97,7 @@ int main(int argc, char **argv) unlink(argv[2]); die("error opening %s: %s\n", argv[1], strerror(errno)); } - +/* - comment out this outdated shit if (!strcmp(argv[1], "system") || !strcmp(argv[1], "cache") || !strcmp(argv[1], "userdata")) { @@ -107,6 +107,13 @@ int main(int argc, char **argv) read_size = BLOCK_SIZE; read_func = mtd_read_data; } +*/ + +// - use mtd_read_data only, clean this up later + read_size = BLOCK_SIZE; + read_func = mtd_read_data; + +// printf("debug: partition size: %d\n", partition_size); total = 0; while ((len = read_func(in, buf, read_size)) > 0) { @@ -119,12 +126,15 @@ int main(int argc, char **argv) total += BLOCK_SIZE; } +// printf("debug: bytes read: %d\n", total); + +/* packetlss - don't assume every eraseblock is ok if (total != partition_size) { close(fd); unlink(argv[2]); die("error reading %s", argv[1]); } - +*/ mtd_read_close(in); if (close(fd)) { diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c index 25feabd..18e6a5d 100644 --- a/mtdutils/mtdutils.c +++ b/mtdutils/mtdutils.c @@ -38,7 +38,6 @@ struct MtdPartition { struct MtdReadContext { const MtdPartition *partition; char *buffer; - size_t read_size; size_t consumed; int fd; }; @@ -565,84 +564,3 @@ off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) { } return pos; } - - -ssize_t mtd_read_raw(MtdReadContext *ctx, char *data, size_t len) -{ - static const int SPARE_SIZE = (2048 >> 5); - struct mtd_info_user mtd_info; - struct mtd_oob_buf oob_buf; - struct nand_ecclayout ecc_layout; - struct nand_oobfree *fp; - unsigned char ecc[SPARE_SIZE]; - char *src, *dst; - int i, n, ret; - -/* - * FIXME: These two ioctls should be cached in MtdReadContext. - */ - ret = ioctl(ctx->fd, MEMGETINFO, &mtd_info); - if (ret < 0) - return -1; - - ret = ioctl(ctx->fd, ECCGETLAYOUT, &ecc_layout); - if (ret < 0) - return -1; - - ctx->read_size = mtd_info.writesize; - ctx->consumed = ctx->read_size; - -/* - * Read next good data block. - */ - ret = read_block(ctx->partition, ctx->fd, data); - if (ret < 0) - return -1; - - dst = src = data + ctx->read_size; - -/* - * Read OOB data for last block read in read_block(). - */ - oob_buf.start = lseek(ctx->fd, 0, SEEK_CUR) - ctx->read_size; - oob_buf.length = mtd_info.oobsize; - oob_buf.ptr = (unsigned char *) src; - - ret = ioctl(ctx->fd, MEMREADOOB, &oob_buf); - if (ret < 0) - return -1; - -/* - * As yaffs and yaffs2 use mode MEM_OOB_AUTO, but mtdchar uses - * MEM_OOB_PLACE, copy the spare data down the hard way. - * - * Safe away ECC data: - */ - for (i = 0; i < ecc_layout.eccbytes; i++) { - ecc[i] = src[ecc_layout.eccpos[i]]; - } - for ( ; i < SPARE_SIZE; i++) { - ecc[i] = 0; - } - -/* - * Copy yaffs2 spare data down. - */ - n = ecc_layout.oobavail; - fp = &ecc_layout.oobfree[0]; - while (n) { - if (fp->offset) { - memmove(dst, src + fp->offset, fp->length); - } - dst += fp->length; - n -= fp->length; - ++fp; - } - -/* - * Restore ECC data behind spare data. - */ - memcpy(dst, ecc, (ctx->read_size >> 5) - ecc_layout.oobavail); - - return ctx->read_size + (ctx->read_size >> 5); -} From ff32e8c7e506f00717c2a39765e22ef63451efea Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Wed, 24 Feb 2010 13:16:43 -0800 Subject: [PATCH 027/233] clean up --- mtdutils/dump_image.c | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/mtdutils/dump_image.c b/mtdutils/dump_image.c index ddaf6f6..5e740f2 100644 --- a/mtdutils/dump_image.c +++ b/mtdutils/dump_image.c @@ -97,26 +97,9 @@ int main(int argc, char **argv) unlink(argv[2]); die("error opening %s: %s\n", argv[1], strerror(errno)); } -/* - comment out this outdated shit - if (!strcmp(argv[1], "system") || - !strcmp(argv[1], "cache") || - !strcmp(argv[1], "userdata")) { - read_size = BLOCK_SIZE + SPARE_SIZE; - read_func = mtd_read_raw; - } else { - read_size = BLOCK_SIZE; - read_func = mtd_read_data; - } -*/ - -// - use mtd_read_data only, clean this up later - read_size = BLOCK_SIZE; - read_func = mtd_read_data; - -// printf("debug: partition size: %d\n", partition_size); total = 0; - while ((len = read_func(in, buf, read_size)) > 0) { + while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) { wrote = write(fd, buf, len); if (wrote != len) { close(fd); @@ -126,15 +109,6 @@ int main(int argc, char **argv) total += BLOCK_SIZE; } -// printf("debug: bytes read: %d\n", total); - -/* packetlss - don't assume every eraseblock is ok - if (total != partition_size) { - close(fd); - unlink(argv[2]); - die("error reading %s", argv[1]); - } -*/ mtd_read_close(in); if (close(fd)) { From 36d02893cd2490e565e6880dd6d030912bd93a63 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Mon, 22 Feb 2010 15:08:23 -0800 Subject: [PATCH 028/233] fix build --- mtdutils/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk index c45a75d..bbdc72f 100644 --- a/mtdutils/Android.mk +++ b/mtdutils/Android.mk @@ -63,6 +63,7 @@ LOCAL_MODULE_TAGS := eng LOCAL_STATIC_LIBRARIES := recovery_mkyaffs2image recovery_unyaffs recovery_dump_image recovery_flash_image libmtdutils libcutils libc LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_UNSTRIPPED_PATH := $(TARGET_OUT_EXECUTABLES_UNSTRIPPED) ADDITIONAL_RECOVERY_EXECUTABLES += recovery_tools include $(BUILD_EXECUTABLE) From 01098d245c8c3a5564139f097ac7390c318b090d Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Thu, 25 Feb 2010 11:07:19 -0800 Subject: [PATCH 029/233] nandroid backup supports a directory now --- nandroid/nandroid-mobile.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nandroid/nandroid-mobile.sh b/nandroid/nandroid-mobile.sh index 738299d..4f42241 100755 --- a/nandroid/nandroid-mobile.sh +++ b/nandroid/nandroid-mobile.sh @@ -193,7 +193,12 @@ case $FAIL in esac TIMESTAMP="`date +%Y%m%d-%H%M`" -DESTDIR="/sdcard/nandroid/$TIMESTAMP" +BASEDIR=/sdcard/nandroid +if [ !-z "$2" ]; then + BASEDIR=$2 +fi + +DESTDIR=$BASEDIR/$TIMESTAMP if [ ! -d $DESTDIR ]; then mkdir -p $DESTDIR if [ ! -d $DESTDIR ]; then From 33370db18724dc2720a6a45fd9cbd8c1a291e0f6 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Thu, 25 Feb 2010 11:39:07 -0800 Subject: [PATCH 030/233] better error codes to allow for diagnosing problems in recovery mode. fix system looking for sh in /system/bin/sh --- extendedcommands.c | 49 +++++++++++++++++++++++++++++++++++-- nandroid/nandroid-mobile.sh | 41 ++++++++++++++++--------------- 2 files changed, 68 insertions(+), 22 deletions(-) diff --git a/extendedcommands.c b/extendedcommands.c index 61b0252..1f32c49 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -16,6 +16,9 @@ #include #include +#include +#include + #include "bootloader.h" #include "common.h" #include "cutils/properties.h" @@ -195,12 +198,54 @@ void show_choose_zip_menu() install_zip(sdcard_package_file); } +// This was pulled from bionic: The default system command always looks +// for shell in /system/bin/sh. This is bad. +#define _PATH_BSHELL "/sbin/sh" +#define system recovery_system +extern char **environ; +int +system(const char *command) +{ + pid_t pid; + sig_t intsave, quitsave; + sigset_t mask, omask; + int pstat; + char *argp[] = {"sh", "-c", NULL, NULL}; + + if (!command) /* just checking... */ + return(1); + + argp[2] = (char *)command; + + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + sigprocmask(SIG_BLOCK, &mask, &omask); + switch (pid = vfork()) { + case -1: /* error */ + sigprocmask(SIG_SETMASK, &omask, NULL); + return(-1); + case 0: /* child */ + sigprocmask(SIG_SETMASK, &omask, NULL); + execve(_PATH_BSHELL, argp, environ); + _exit(127); + } + + intsave = (sig_t) bsd_signal(SIGINT, SIG_IGN); + quitsave = (sig_t) bsd_signal(SIGQUIT, SIG_IGN); + pid = waitpid(pid, (int *)&pstat, 0); + sigprocmask(SIG_SETMASK, &omask, NULL); + (void)bsd_signal(SIGINT, intsave); + (void)bsd_signal(SIGQUIT, quitsave); + return (pid == -1 ? -1 : pstat); +} + void do_nandroid_backup() { ui_print("Performing backup...\n"); - if (system("/sbin/nandroid-mobile.sh backup") != 0) + int ret = system("/sbin/nandroid-mobile.sh backup /sdcard/clockworkmod/backup/"); + if (ret != 0) { - ui_print("Error while backing up!\n"); + ui_print("Error while backing up! Error code: %d\n", ret); return; } ui_print("Backup complete.\n"); diff --git a/nandroid/nandroid-mobile.sh b/nandroid/nandroid-mobile.sh index 4f42241..6de3a8c 100755 --- a/nandroid/nandroid-mobile.sh +++ b/nandroid/nandroid-mobile.sh @@ -55,6 +55,7 @@ RECOVERY=foo echo "nandroid-mobile v2.1" +echo here > /etc/foo mkfstab.sh > /etc/fstab if [ "$1" == "" ]; then @@ -79,7 +80,7 @@ case $1 in dump_image=`which dump_image-arm-uclibc` if [ "$dump_image" == "" ]; then echo "error: dump_image or dump_image-arm-uclibc not found in path" - exit 1 + exit 2 fi fi break @@ -90,13 +91,13 @@ case $1 in flash_image=`which flash_image-arm-uclibc` if [ "$flash_image" == "" ]; then echo "error: flash_image or flash_image-arm-uclibc not found in path" - exit 1 + exit 3 fi fi unyaffs=`which unyaffs` if [ "$unyaffs" == "" ]; then echo "error: unyaffs not found in path" - exit 1 + exit 4 fi break ;; @@ -106,12 +107,12 @@ esac RECOVERY=`cat /proc/cmdline | grep "androidboot.mode=recovery"` if [ "$RECOVERY" == "foo" ]; then echo "error: not running in recovery mode, aborting" - exit 1 + exit 5 fi if [ ! "`id -u 2>/dev/null`" == "0" ]; then if [ "`whoami 2>&1 | grep 'uid 0'`" == "" ]; then echo "error: must run as root, aborting" - exit 1 + exit 6 fi fi @@ -125,20 +126,20 @@ case $1 in if [ ! $ENERGY -ge 30 ]; then echo "Error: not enough battery power" echo "Connect charger or USB power and try again" - exit 1 + exit 7 fi RESTOREPATH=$2 if [ ! -f $RESTOREPATH/nandroid.md5 ]; then echo "error: $RESTOREPATH/nandroid.md5 not found, cannot verify backup data" - exit 1 + exit 8 fi umount /system 2>/dev/null umount /data 2>/dev/null mount -o rw /system || FAIL=1 mount -o rw /data || FAIL=2 case $FAIL in - 1) echo "Error mounting system read-write"; umount /system /data /cache; exit 1;; - 2) echo "Error mounting data read-write"; umount /system /data /cache; exit 1;; + 1) echo "Error mounting system read-write"; umount /system /data /cache; exit 9;; + 2) echo "Error mounting data read-write"; umount /system /data /cache; exit 10;; esac echo "Verifying backup images..." @@ -147,7 +148,7 @@ case $1 in md5sum -c nandroid.md5 if [ $? -eq 1 ]; then echo "error: md5sum mismatch, aborting" - exit 1 + exit 11 fi for image in boot; do echo "Flashing $image..." @@ -174,7 +175,7 @@ case $1 in echo "Usage: $0 {backup|restore} [/path/to/nandroid/backup/]" echo "- backup will store a full system backup on /sdcard/nandroid" echo "- restore path will restore the last made backup for boot, system, recovery and data" - exit 1 + exit 12 ;; esac @@ -185,16 +186,16 @@ umount /data 2>/dev/null umount /sdcard 2>/dev/null mount -o ro /system || FAIL=1 mount -o ro /data || FAIL=2 -mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=3 +mount /sdcard || FAIL=3 case $FAIL in - 1) echo "Error mounting system read-only"; umount /system /data /sdcard; exit 1;; - 2) echo "Error mounting data read-only"; umount /system /data /sdcard; exit 1;; - 3) echo "Error mounting sdcard read-write"; umount /system /data /sdcard; exit 1;; + 1) echo "Error mounting system read-only"; umount /system /data /sdcard; exit 13;; + 2) echo "Error mounting data read-only"; umount /system /data /sdcard; exit 14;; + 3) echo "Error mounting sdcard read-write"; umount /system /data /sdcard; exit 15;; esac TIMESTAMP="`date +%Y%m%d-%H%M`" BASEDIR=/sdcard/nandroid -if [ !-z "$2" ]; then +if [ ! -z "$2" ]; then BASEDIR=$2 fi @@ -206,7 +207,7 @@ if [ ! -d $DESTDIR ]; then umount /system 2>/dev/null umount /data 2>/dev/null umount /sdcard 2>/dev/null - exit 1 + exit 16 fi else touch $DESTDIR/.nandroidwritable @@ -215,7 +216,7 @@ else umount /system 2>/dev/null umount /data 2>/dev/null umount /sdcard 2>/dev/null - exit 1 + exit 16 fi rm $DESTDIR/.nandroidwritable fi @@ -229,7 +230,7 @@ if [ $FREEBLOCKS -le 130000 ]; then umount /system 2>/dev/null umount /data 2>/dev/null umount /sdcard 2>/dev/null - exit 1 + exit 17 fi @@ -271,7 +272,7 @@ for image in boot recovery misc; do umount /system umount /data umount /sdcard - exit 1 + exit 18 fi done echo "done" From 79ce82cab33d8497219557289aac8dc661805311 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Thu, 25 Feb 2010 12:03:17 -0800 Subject: [PATCH 031/233] successful installation should hide the android installing icon --- extendedcommands.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/extendedcommands.c b/extendedcommands.c index 1f32c49..d54321f 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -54,16 +54,13 @@ void install_zip(const char* packagefilepath) if (status != INSTALL_SUCCESS) { ui_set_background(BACKGROUND_ICON_ERROR); ui_print("Installation aborted.\n"); - } else if (!ui_text_visible()) { - return; // reboot if logs aren't visible - } else { - if (firmware_update_pending()) { - ui_print("\nReboot via menu to complete\n" - "installation.\n"); - } else { - ui_print("\nInstall from sdcard complete.\n"); - } - } + return; + } + if (firmware_update_pending()) { + ui_print("\nReboot via menu to complete\ninstallation.\n"); + } + ui_print("\nInstall from sdcard complete.\n"); + ui_set_background(BACKGROUND_ICON_NONE); } char* INSTALL_MENU_ITEMS[] = { "apply sdcard:update.zip", From 49f5689b42b6f66b7821c3bc1340f0fa79721041 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Thu, 25 Feb 2010 14:51:05 -0800 Subject: [PATCH 032/233] allow choosing of subdirectories when searching for a zip file to install --- extendedcommands.c | 147 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 116 insertions(+), 31 deletions(-) diff --git a/extendedcommands.c b/extendedcommands.c index d54321f..7c60999 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -103,7 +104,7 @@ void show_install_update_menu() } } -char* choose_file_menu(const char* directory, const char* extension, const char* headers[]) +char** gather_files(const char* directory, const char* fileExtensionOrDirectory, int* numFiles) { char path[PATH_MAX] = ""; DIR *dir; @@ -111,7 +112,9 @@ char* choose_file_menu(const char* directory, const char* extension, const char* int total = 0; int i; char** files; - char** list; + int pass; + *numFiles = 0; + int dirLen = strlen(directory); dir = opendir(directory); if (dir == NULL) { @@ -119,47 +122,121 @@ char* choose_file_menu(const char* directory, const char* extension, const char* return NULL; } - const int extension_length = strlen(extension); + int extension_length; + if (fileExtensionOrDirectory != NULL) + extension_length = strlen(fileExtensionOrDirectory); - while ((de=readdir(dir)) != NULL) { - if (de->d_name[0] != '.' && strlen(de->d_name) > extension_length && strcmp(de->d_name + strlen(de->d_name) - extension_length, extension) == 0) { - total++; - } + int isCounting = 1; + i = 0; + for (pass = 0; pass < 2; pass++) { + while ((de=readdir(dir)) != NULL) { + // skip hidden files + if (de->d_name[0] == '.') + continue; + + // NULL means that we are gathering directories, so skip this + if (fileExtensionOrDirectory != NULL) + { + // make sure that we can have the desired extension (prevent seg fault) + if (strlen(de->d_name) < extension_length) + continue; + // compare the extension + if (strcmp(de->d_name + strlen(de->d_name) - extension_length, fileExtensionOrDirectory) != 0) + continue; + } + else + { + struct stat info; + char* fullFileName = (char*)malloc(strlen(de->d_name) + dirLen + 1); + strcpy(fullFileName, directory); + strcat(fullFileName, de->d_name); + stat(fullFileName, &info); + free(fullFileName); + // make sure it is a directory + if (!(S_ISDIR(info.st_mode))) + continue; + } + + if (pass == 0) + { + total++; + continue; + } + + files[i] = (char*) malloc(dirLen + strlen(de->d_name) + 2); + strcpy(files[i], directory); + strcat(files[i], de->d_name); + if (fileExtensionOrDirectory == NULL) + strcat(files[i], "/"); + i++; + } + if (pass == 1) + break; + if (total == 0) + break; + rewinddir(dir); + *numFiles = total; + files = (char**) malloc((total+1)*sizeof(char*)); + files[total]=NULL; + } + + if(closedir(dir) < 0) { + LOGE("Failed to close directory."); } if (total==0) { - ui_print("No files found.\n"); - if(closedir(dir) < 0) { - LOGE("Failed to close directory."); - } return NULL; } - files = (char**) malloc((total+1)*sizeof(char*)); - files[total]=NULL; + return files; +} - list = (char**) malloc((total+1)*sizeof(char*)); - list[total]=NULL; +void free_string_array(char** array) +{ + char* cursor = array[0]; + int i = 0; + while (cursor != NULL) + { + free(cursor); + cursor = array[++i]; + } + free(array); +} - rewinddir(dir); +// pass in NULL for fileExtensionOrDirectory and you will get a directory chooser +char* choose_file_menu(const char* directory, const char* fileExtensionOrDirectory, const char* headers[]) +{ + char path[PATH_MAX] = ""; + DIR *dir; + struct dirent *de; + int numFiles = 0; + int numDirs = 0; + int i; - i = 0; - while ((de = readdir(dir)) != NULL) { - if (de->d_name[0] != '.' && strlen(de->d_name) > extension_length && strcmp(de->d_name + strlen(de->d_name) - extension_length, extension) == 0) { - files[i] = (char*) malloc(strlen(directory)+strlen(de->d_name)+1); - strcpy(files[i], directory); - strcat(files[i], de->d_name); + int dir_len = strlen(directory); - list[i] = (char*) malloc(strlen(de->d_name)+1); - strcpy(list[i], de->d_name); + char** files = gather_files(directory, fileExtensionOrDirectory, &numFiles); + char** dirs; + if (fileExtensionOrDirectory != NULL) + dirs = gather_files(directory, NULL, &numDirs); + int total = numDirs + numFiles; + if (total == 0) + { + ui_print("No files found.\n"); + return NULL; + } + char** list = (char**) malloc((total + 1) * sizeof(char*)); + list[total] = NULL; - i++; - } + + for (i = 0 ; i < numDirs; i++) + { + list[i] = strdup(dirs[i] + dir_len); } - if (closedir(dir) <0) { - LOGE("Failure closing directory."); - return NULL; + for (i = 0 ; i < numFiles; i++) + { + list[numDirs + i] = strdup(files[i] + dir_len); } for (;;) @@ -167,8 +244,16 @@ char* choose_file_menu(const char* directory, const char* extension, const char* int chosen_item = get_menu_selection(headers, list, 0); if (chosen_item == GO_BACK) break; + if (chosen_item < numDirs) + { + char* subret = choose_file_menu(dirs[chosen_item], fileExtensionOrDirectory, headers); + if (subret != NULL) + return subret; + continue; + } static char ret[PATH_MAX]; - strcpy(ret, files[chosen_item]); + strcpy(ret, files[chosen_item - numDirs]); + ui_print("File chosen: %s\n", ret); return ret; } return NULL; @@ -260,7 +345,7 @@ void show_nandroid_restore_menu() NULL }; - char* file = choose_file_menu("/sdcard/clockworkmod/backup/", "", headers); + char* file = choose_file_menu("/sdcard/clockworkmod/backup/", NULL, headers); if (file == NULL) return; char* command[PATH_MAX]; From 001c5b508f9b385726cbae4870843b40798d1b31 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Thu, 25 Feb 2010 14:53:57 -0800 Subject: [PATCH 033/233] clean up tabs vs spaces --- extendedcommands.c | 244 ++++++++++++++++++++++----------------------- 1 file changed, 122 insertions(+), 122 deletions(-) diff --git a/extendedcommands.c b/extendedcommands.c index 7c60999..4074d38 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -55,13 +55,13 @@ void install_zip(const char* packagefilepath) if (status != INSTALL_SUCCESS) { ui_set_background(BACKGROUND_ICON_ERROR); ui_print("Installation aborted.\n"); - return; + return; } if (firmware_update_pending()) { ui_print("\nReboot via menu to complete\ninstallation.\n"); - } - ui_print("\nInstall from sdcard complete.\n"); - ui_set_background(BACKGROUND_ICON_NONE); + } + ui_print("\nInstall from sdcard complete.\n"); + ui_set_background(BACKGROUND_ICON_NONE); } char* INSTALL_MENU_ITEMS[] = { "apply sdcard:update.zip", @@ -112,9 +112,9 @@ char** gather_files(const char* directory, const char* fileExtensionOrDirectory, int total = 0; int i; char** files; - int pass; - *numFiles = 0; - int dirLen = strlen(directory); + int pass; + *numFiles = 0; + int dirLen = strlen(directory); dir = opendir(directory); if (dir == NULL) { @@ -122,63 +122,63 @@ char** gather_files(const char* directory, const char* fileExtensionOrDirectory, return NULL; } - int extension_length; - if (fileExtensionOrDirectory != NULL) - extension_length = strlen(fileExtensionOrDirectory); + int extension_length; + if (fileExtensionOrDirectory != NULL) + extension_length = strlen(fileExtensionOrDirectory); - int isCounting = 1; - i = 0; - for (pass = 0; pass < 2; pass++) { - while ((de=readdir(dir)) != NULL) { - // skip hidden files - if (de->d_name[0] == '.') - continue; - - // NULL means that we are gathering directories, so skip this - if (fileExtensionOrDirectory != NULL) - { - // make sure that we can have the desired extension (prevent seg fault) - if (strlen(de->d_name) < extension_length) - continue; - // compare the extension - if (strcmp(de->d_name + strlen(de->d_name) - extension_length, fileExtensionOrDirectory) != 0) - continue; - } - else - { - struct stat info; - char* fullFileName = (char*)malloc(strlen(de->d_name) + dirLen + 1); - strcpy(fullFileName, directory); - strcat(fullFileName, de->d_name); - stat(fullFileName, &info); - free(fullFileName); - // make sure it is a directory - if (!(S_ISDIR(info.st_mode))) - continue; - } - - if (pass == 0) - { - total++; - continue; - } - + int isCounting = 1; + i = 0; + for (pass = 0; pass < 2; pass++) { + while ((de=readdir(dir)) != NULL) { + // skip hidden files + if (de->d_name[0] == '.') + continue; + + // NULL means that we are gathering directories, so skip this + if (fileExtensionOrDirectory != NULL) + { + // make sure that we can have the desired extension (prevent seg fault) + if (strlen(de->d_name) < extension_length) + continue; + // compare the extension + if (strcmp(de->d_name + strlen(de->d_name) - extension_length, fileExtensionOrDirectory) != 0) + continue; + } + else + { + struct stat info; + char* fullFileName = (char*)malloc(strlen(de->d_name) + dirLen + 1); + strcpy(fullFileName, directory); + strcat(fullFileName, de->d_name); + stat(fullFileName, &info); + free(fullFileName); + // make sure it is a directory + if (!(S_ISDIR(info.st_mode))) + continue; + } + + if (pass == 0) + { + total++; + continue; + } + files[i] = (char*) malloc(dirLen + strlen(de->d_name) + 2); strcpy(files[i], directory); strcat(files[i], de->d_name); - if (fileExtensionOrDirectory == NULL) - strcat(files[i], "/"); - i++; - } - if (pass == 1) - break; - if (total == 0) - break; - rewinddir(dir); - *numFiles = total; - files = (char**) malloc((total+1)*sizeof(char*)); - files[total]=NULL; - } + if (fileExtensionOrDirectory == NULL) + strcat(files[i], "/"); + i++; + } + if (pass == 1) + break; + if (total == 0) + break; + rewinddir(dir); + *numFiles = total; + files = (char**) malloc((total+1)*sizeof(char*)); + files[total]=NULL; + } if(closedir(dir) < 0) { LOGE("Failed to close directory."); @@ -188,19 +188,19 @@ char** gather_files(const char* directory, const char* fileExtensionOrDirectory, return NULL; } - return files; + return files; } void free_string_array(char** array) { - char* cursor = array[0]; - int i = 0; - while (cursor != NULL) - { - free(cursor); - cursor = array[++i]; - } - free(array); + char* cursor = array[0]; + int i = 0; + while (cursor != NULL) + { + free(cursor); + cursor = array[++i]; + } + free(array); } // pass in NULL for fileExtensionOrDirectory and you will get a directory chooser @@ -210,33 +210,33 @@ char* choose_file_menu(const char* directory, const char* fileExtensionOrDirecto DIR *dir; struct dirent *de; int numFiles = 0; - int numDirs = 0; + int numDirs = 0; int i; - int dir_len = strlen(directory); + int dir_len = strlen(directory); - char** files = gather_files(directory, fileExtensionOrDirectory, &numFiles); - char** dirs; - if (fileExtensionOrDirectory != NULL) - dirs = gather_files(directory, NULL, &numDirs); - int total = numDirs + numFiles; - if (total == 0) - { - ui_print("No files found.\n"); - return NULL; - } - char** list = (char**) malloc((total + 1) * sizeof(char*)); - list[total] = NULL; + char** files = gather_files(directory, fileExtensionOrDirectory, &numFiles); + char** dirs; + if (fileExtensionOrDirectory != NULL) + dirs = gather_files(directory, NULL, &numDirs); + int total = numDirs + numFiles; + if (total == 0) + { + ui_print("No files found.\n"); + return NULL; + } + char** list = (char**) malloc((total + 1) * sizeof(char*)); + list[total] = NULL; - for (i = 0 ; i < numDirs; i++) - { - list[i] = strdup(dirs[i] + dir_len); + for (i = 0 ; i < numDirs; i++) + { + list[i] = strdup(dirs[i] + dir_len); } - for (i = 0 ; i < numFiles; i++) - { - list[numDirs + i] = strdup(files[i] + dir_len); + for (i = 0 ; i < numFiles; i++) + { + list[numDirs + i] = strdup(files[i] + dir_len); } for (;;) @@ -244,16 +244,16 @@ char* choose_file_menu(const char* directory, const char* fileExtensionOrDirecto int chosen_item = get_menu_selection(headers, list, 0); if (chosen_item == GO_BACK) break; - if (chosen_item < numDirs) - { - char* subret = choose_file_menu(dirs[chosen_item], fileExtensionOrDirectory, headers); - if (subret != NULL) - return subret; - continue; - } + if (chosen_item < numDirs) + { + char* subret = choose_file_menu(dirs[chosen_item], fileExtensionOrDirectory, headers); + if (subret != NULL) + return subret; + continue; + } static char ret[PATH_MAX]; strcpy(ret, files[chosen_item - numDirs]); - ui_print("File chosen: %s\n", ret); + ui_print("File chosen: %s\n", ret); return ret; } return NULL; @@ -289,42 +289,42 @@ int system(const char *command) { pid_t pid; - sig_t intsave, quitsave; - sigset_t mask, omask; - int pstat; - char *argp[] = {"sh", "-c", NULL, NULL}; + sig_t intsave, quitsave; + sigset_t mask, omask; + int pstat; + char *argp[] = {"sh", "-c", NULL, NULL}; - if (!command) /* just checking... */ - return(1); + if (!command) /* just checking... */ + return(1); - argp[2] = (char *)command; + argp[2] = (char *)command; - sigemptyset(&mask); - sigaddset(&mask, SIGCHLD); - sigprocmask(SIG_BLOCK, &mask, &omask); - switch (pid = vfork()) { - case -1: /* error */ - sigprocmask(SIG_SETMASK, &omask, NULL); - return(-1); - case 0: /* child */ - sigprocmask(SIG_SETMASK, &omask, NULL); - execve(_PATH_BSHELL, argp, environ); + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + sigprocmask(SIG_BLOCK, &mask, &omask); + switch (pid = vfork()) { + case -1: /* error */ + sigprocmask(SIG_SETMASK, &omask, NULL); + return(-1); + case 0: /* child */ + sigprocmask(SIG_SETMASK, &omask, NULL); + execve(_PATH_BSHELL, argp, environ); _exit(127); } - intsave = (sig_t) bsd_signal(SIGINT, SIG_IGN); - quitsave = (sig_t) bsd_signal(SIGQUIT, SIG_IGN); - pid = waitpid(pid, (int *)&pstat, 0); - sigprocmask(SIG_SETMASK, &omask, NULL); - (void)bsd_signal(SIGINT, intsave); - (void)bsd_signal(SIGQUIT, quitsave); - return (pid == -1 ? -1 : pstat); + intsave = (sig_t) bsd_signal(SIGINT, SIG_IGN); + quitsave = (sig_t) bsd_signal(SIGQUIT, SIG_IGN); + pid = waitpid(pid, (int *)&pstat, 0); + sigprocmask(SIG_SETMASK, &omask, NULL); + (void)bsd_signal(SIGINT, intsave); + (void)bsd_signal(SIGQUIT, quitsave); + return (pid == -1 ? -1 : pstat); } void do_nandroid_backup() { ui_print("Performing backup...\n"); - int ret = system("/sbin/nandroid-mobile.sh backup /sdcard/clockworkmod/backup/"); + int ret = system("/sbin/nandroid-mobile.sh backup /sdcard/clockworkmod/backup/"); if (ret != 0) { ui_print("Error while backing up! Error code: %d\n", ret); From 261dde9f48c20dd102e274bd1939dbb0eff6b38e Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Thu, 25 Feb 2010 16:51:45 -0800 Subject: [PATCH 034/233] make it pretty --- Android.mk | 2 +- default_recovery_ui.c | 4 +--- nandroid/nandroid-mobile.sh | 3 +-- recovery.c | 9 +++++---- ui.c | 22 ++++++++++++++++++---- 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/Android.mk b/Android.mk index 688acb9..2e565bb 100644 --- a/Android.mk +++ b/Android.mk @@ -25,7 +25,7 @@ LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true -RECOVERY_API_VERSION := 2 +RECOVERY_API_VERSION := 1.4 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) # This binary is in the recovery ramdisk, which is otherwise a copy of root. diff --git a/default_recovery_ui.c b/default_recovery_ui.c index d832794..2b78e0c 100644 --- a/default_recovery_ui.c +++ b/default_recovery_ui.c @@ -20,9 +20,7 @@ #include "common.h" #include "extendedcommands.h" -char* MENU_HEADERS[] = { "Android system recovery utility", - "", - NULL }; +char* MENU_HEADERS[] = { NULL }; char* MENU_ITEMS[] = { "reboot system now", "apply sdcard:update.zip", diff --git a/nandroid/nandroid-mobile.sh b/nandroid/nandroid-mobile.sh index 6de3a8c..3f516d6 100755 --- a/nandroid/nandroid-mobile.sh +++ b/nandroid/nandroid-mobile.sh @@ -55,7 +55,6 @@ RECOVERY=foo echo "nandroid-mobile v2.1" -echo here > /etc/foo mkfstab.sh > /etc/fstab if [ "$1" == "" ]; then @@ -193,7 +192,7 @@ case $FAIL in 3) echo "Error mounting sdcard read-write"; umount /system /data /sdcard; exit 15;; esac -TIMESTAMP="`date +%Y%m%d-%H%M`" +TIMESTAMP="`date +%Y-%m-%d-%H%M`" BASEDIR=/sdcard/nandroid if [ ! -z "$2" ]; then BASEDIR=$2 diff --git a/recovery.c b/recovery.c index 45911ea..6b16470 100644 --- a/recovery.c +++ b/recovery.c @@ -136,7 +136,7 @@ fopen_root_path(const char *root_path, const char *mode) { if (strchr("wa", mode[0])) dirCreateHierarchy(path, 0777, NULL, 1); FILE *fp = fopen(path, mode); - if (fp == NULL) LOGE("Can't open %s\n", path); + if (fp == NULL && root_path != COMMAND_FILE) LOGE("Can't open %s\n", path); return fp; } @@ -284,8 +284,8 @@ erase_root(const char *root) static char** prepend_title(char** headers) { - char* title[] = { "Android system recovery <" - EXPAND(RECOVERY_API_VERSION) "e>", + char* title[] = { "ClockworkMod Recovery v" + EXPAND(RECOVERY_API_VERSION), "", NULL }; @@ -393,7 +393,8 @@ static void prompt_and_wait() { char** headers = prepend_title(MENU_HEADERS); - + ui_print("ClockworkMod Recovery v"EXPAND(RECOVERY_API_VERSION)"\n"); + for (;;) { finish_recovery(NULL); ui_reset_progress(); diff --git a/ui.c b/ui.c index 70ddc33..fdb11f4 100644 --- a/ui.c +++ b/ui.c @@ -164,6 +164,10 @@ static void draw_text_line(int row, const char* t) { } } +#define MENU_TEXT_COLOR 7, 133, 74, 255 +#define NORMAL_TEXT_COLOR 200, 200, 200, 255 +#define HEADER_TEXT_COLOR NORMAL_TEXT_COLOR + // Redraw everything on the screen. Does not flip pages. // Should only be called with gUpdateMutex locked. static void draw_screen_locked(void) @@ -177,15 +181,21 @@ static void draw_screen_locked(void) int i = 0; if (show_menu) { - gr_color(64, 96, 255, 255); + gr_color(MENU_TEXT_COLOR); gr_fill(0, (menu_top+menu_sel) * CHAR_HEIGHT, gr_fb_width(), (menu_top+menu_sel+1)*CHAR_HEIGHT+1); + gr_color(HEADER_TEXT_COLOR); + int is_drawing_header = 1; for (; i < menu_top + menu_items; ++i) { + if (is_drawing_header && strcmp(menu[i], "") == 0) { + gr_color(MENU_TEXT_COLOR); + is_drawing_header = 0; + } if (i == menu_top + menu_sel) { gr_color(255, 255, 255, 255); draw_text_line(i, menu[i]); - gr_color(64, 96, 255, 255); + gr_color(MENU_TEXT_COLOR); } else { draw_text_line(i, menu[i]); } @@ -195,7 +205,7 @@ static void draw_screen_locked(void) ++i; } - gr_color(255, 255, 0, 255); + gr_color(NORMAL_TEXT_COLOR); for (; i < text_rows; ++i) { draw_text_line(i, text[(i+text_top) % text_rows]); @@ -458,6 +468,9 @@ void ui_print(const char *fmt, ...) pthread_mutex_unlock(&gUpdateMutex); } +#define MENU_ITEM_HEADER " - " +#define MENU_ITEM_HEADER_LENGTH strlen(MENU_ITEM_HEADER) + void ui_start_menu(char** headers, char** items) { int i; pthread_mutex_lock(&gUpdateMutex); @@ -470,7 +483,8 @@ void ui_start_menu(char** headers, char** items) { menu_top = i; for (; i < text_rows; ++i) { if (items[i-menu_top] == NULL) break; - strncpy(menu[i], items[i-menu_top], text_cols-1); + strcpy(menu[i], MENU_ITEM_HEADER); + strncpy(menu[i] + MENU_ITEM_HEADER_LENGTH, items[i-menu_top], text_cols-1 - MENU_ITEM_HEADER_LENGTH); menu[i][text_cols-1] = '\0'; } menu_items = i - menu_top; From 185baeeb7940f7b88f96143f849b80069dd1b8a9 Mon Sep 17 00:00:00 2001 From: Chris Soyars Date: Fri, 26 Feb 2010 02:45:55 -0500 Subject: [PATCH 035/233] Added ability to wipe_data and update_package at the same time. This will be needed for a future project I am working on, OpenUpdater. --- recovery.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/recovery.c b/recovery.c index 6b16470..1f2c83f 100644 --- a/recovery.c +++ b/recovery.c @@ -515,6 +515,13 @@ main(int argc, char **argv) } if (update_package != NULL) { + if (wipe_data && erase_root("DATA:")) status = INSTALL_ERROR; + status = install_package(update_package); + if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n"); + } else if (update_package != NULL && wipe_data) { + if (device_wipe_data()) status = INSTALL_ERROR; + if (erase_root("DATA:")) status = INSTALL_ERROR; + if (wipe_cache && erase_root("CACHE:")) status = INSTALL_ERROR; status = install_package(update_package); if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n"); } else if (wipe_data) { @@ -545,4 +552,4 @@ main(int argc, char **argv) int get_allow_toggle_display() { return allow_display_toggle; -} \ No newline at end of file +} From db8dedf12f9aabce1a95c715af6da9aeeae7155f Mon Sep 17 00:00:00 2001 From: Chris Soyars Date: Fri, 26 Feb 2010 02:48:08 -0500 Subject: [PATCH 036/233] Fixed a bug that would never allow update_package + wipe_data to be called. --- recovery.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/recovery.c b/recovery.c index 1f2c83f..7759568 100644 --- a/recovery.c +++ b/recovery.c @@ -518,12 +518,6 @@ main(int argc, char **argv) if (wipe_data && erase_root("DATA:")) status = INSTALL_ERROR; status = install_package(update_package); if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n"); - } else if (update_package != NULL && wipe_data) { - if (device_wipe_data()) status = INSTALL_ERROR; - if (erase_root("DATA:")) status = INSTALL_ERROR; - if (wipe_cache && erase_root("CACHE:")) status = INSTALL_ERROR; - status = install_package(update_package); - if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n"); } else if (wipe_data) { if (device_wipe_data()) status = INSTALL_ERROR; if (erase_root("DATA:")) status = INSTALL_ERROR; From a1749d93bc6462442cd07084b1277d9a302f09cd Mon Sep 17 00:00:00 2001 From: Chris Soyars Date: Fri, 26 Feb 2010 02:45:55 -0500 Subject: [PATCH 037/233] Added ability to wipe_data and update_package at the same time. This will be needed for a future project I am working on, OpenUpdater. --- recovery.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/recovery.c b/recovery.c index 6b16470..7759568 100644 --- a/recovery.c +++ b/recovery.c @@ -515,6 +515,7 @@ main(int argc, char **argv) } if (update_package != NULL) { + if (wipe_data && erase_root("DATA:")) status = INSTALL_ERROR; status = install_package(update_package); if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n"); } else if (wipe_data) { @@ -545,4 +546,4 @@ main(int argc, char **argv) int get_allow_toggle_display() { return allow_display_toggle; -} \ No newline at end of file +} From 0317378bf6f83fc3feb4525aab8f284c5d8811fc Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Fri, 26 Feb 2010 14:14:23 -0800 Subject: [PATCH 038/233] usb mass storage support. user initiated recovery is now user friendly. --- default_recovery_ui.c | 3 ++- extendedcommands.c | 22 ++++++++++++++++++++++ extendedcommands.h | 9 ++++++--- recovery.c | 11 ++++++++++- recovery_ui.h | 1 + ui.c | 4 ++-- 6 files changed, 43 insertions(+), 7 deletions(-) diff --git a/default_recovery_ui.c b/default_recovery_ui.c index 2b78e0c..458c817 100644 --- a/default_recovery_ui.c +++ b/default_recovery_ui.c @@ -29,7 +29,8 @@ char* MENU_ITEMS[] = { "reboot system now", "install zip from sdcard", "backup", "restore", - "mount sdcard", + "mount /sdcard", + "mount USB storage", NULL }; int device_toggle_display(volatile char* key_pressed, int key_code) { diff --git a/extendedcommands.c b/extendedcommands.c index 4074d38..a63163a 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -358,4 +358,26 @@ void show_nandroid_restore_menu() return; } ui_print("Restore complete.\n"); +} + +void do_mount_usb_storage() +{ + system("echo /dev/block/mmcblk0 > /sys/devices/platform/usb_mass_storage/lun0/file"); + static char* headers[] = { "USB Mass Storage device", + "Leaving this menu unmount", + "your SD card from your PC.", + "", + NULL + }; + + static char* list[] = { "Unmount", NULL }; + + for (;;) + { + int chosen_item = get_menu_selection(headers, list, 0); + if (chosen_item == GO_BACK || chosen_item == 0) + break; + } + + system("echo 0 > /sys/devices/platform/usb_mass_storage/lun0/enable"); } \ No newline at end of file diff --git a/extendedcommands.h b/extendedcommands.h index 00dff0f..5629c41 100644 --- a/extendedcommands.h +++ b/extendedcommands.h @@ -13,11 +13,14 @@ show_choose_zip_menu(); int get_allow_toggle_display(); -int -ui_get_show_menu(); +void +ui_set_show_text(int value); void do_nandroid_backup(); void -show_nandroid_restore_menu(); \ No newline at end of file +show_nandroid_restore_menu(); + +void +do_mount_usb_storage(); \ No newline at end of file diff --git a/recovery.c b/recovery.c index 7759568..4dd8833 100644 --- a/recovery.c +++ b/recovery.c @@ -456,6 +456,9 @@ prompt_and_wait() LOGE ("Can't mount /sdcard\n"); } break; + case ITEM_MOUNT_USB: + do_mount_usb_storage(); + break; } } } @@ -469,6 +472,7 @@ print_property(const char *key, const char *name, void *cookie) int main(int argc, char **argv) { + int is_user_initiated_recovery = 0; time_t start = time(NULL); // If these fail, there's not really anywhere to complain... @@ -528,9 +532,14 @@ main(int argc, char **argv) if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n"); } else { status = INSTALL_ERROR; // No command specified + // we are starting up in user initiated recovery here + // let's set up some default options + signature_check_enabled = 0; + is_user_initiated_recovery = 1; + ui_set_show_text(1); } - if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR); + if (status != INSTALL_SUCCESS && !is_user_initiated_recovery) ui_set_background(BACKGROUND_ICON_ERROR); if (status != INSTALL_SUCCESS || ui_text_visible()) prompt_and_wait(); // If there is a radio image pending, reboot now to install it. diff --git a/recovery_ui.h b/recovery_ui.h index 7165007..51f8817 100644 --- a/recovery_ui.h +++ b/recovery_ui.h @@ -71,6 +71,7 @@ int device_wipe_data(); #define ITEM_BACKUP 5 #define ITEM_RESTORE 6 #define ITEM_MOUNT_SDCARD 7 +#define ITEM_MOUNT_USB 8 // Header text to display above the main menu. extern char* MENU_HEADERS[]; diff --git a/ui.c b/ui.c index fdb11f4..5d1bf67 100644 --- a/ui.c +++ b/ui.c @@ -553,6 +553,6 @@ void ui_clear_key_queue() { pthread_mutex_unlock(&key_queue_mutex); } -void ui_get_show_menu() { - return show_menu; +void ui_set_show_text(int value) { + show_text = value; } From 81d8ee59bb6fd61a3ff34109f951abace3906ba5 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Fri, 26 Feb 2010 14:18:55 -0800 Subject: [PATCH 039/233] up the version --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 2e565bb..93196f7 100644 --- a/Android.mk +++ b/Android.mk @@ -25,7 +25,7 @@ LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true -RECOVERY_API_VERSION := 1.4 +RECOVERY_API_VERSION := 1.4.1 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) # This binary is in the recovery ramdisk, which is otherwise a copy of root. From e81cb750fd3854a2e39869d600ebb2b7bd23f771 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Fri, 26 Feb 2010 17:44:33 -0800 Subject: [PATCH 040/233] not every implementation has the "enable" file --- extendedcommands.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extendedcommands.c b/extendedcommands.c index a63163a..538cbb6 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -379,5 +379,6 @@ void do_mount_usb_storage() break; } + system("echo '' > /sys/devices/platform/usb_mass_storage/lun0/file"); system("echo 0 > /sys/devices/platform/usb_mass_storage/lun0/enable"); -} \ No newline at end of file +} From 99fb6fef11db8172c7daa219f19161fd432f5c20 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Wed, 3 Mar 2010 00:42:58 -0800 Subject: [PATCH 041/233] merge busybox and other tools into recovery. --- Android.mk | 1 + extendedcommands.c | 3 ++- mtdutils/Android.mk | 22 ++++------------------ recovery.c | 12 ++++++++++++ 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Android.mk b/Android.mk index 93196f7..c92facd 100644 --- a/Android.mk +++ b/Android.mk @@ -41,6 +41,7 @@ ifeq ($(TARGET_RECOVERY_UI_LIB),) else LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB) endif +LOCAL_STATIC_LIBRARIES += libbusybox libclearsilverregex libmkyaffs2image libunyaffs libdump_image libflash_image libmtdutils LOCAL_STATIC_LIBRARIES += libamend LOCAL_STATIC_LIBRARIES += libminzip libunz libmtdutils libmincrypt LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils diff --git a/extendedcommands.c b/extendedcommands.c index 538cbb6..3f52947 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -52,6 +52,7 @@ void install_zip(const char* packagefilepath) ui_print("\n-- Installing: %s\n", packagefilepath); set_sdcard_update_bootloader_message(); int status = install_package(packagefilepath); + ui_reset_progress(); if (status != INSTALL_SUCCESS) { ui_set_background(BACKGROUND_ICON_ERROR); ui_print("Installation aborted.\n"); @@ -60,8 +61,8 @@ void install_zip(const char* packagefilepath) if (firmware_update_pending()) { ui_print("\nReboot via menu to complete\ninstallation.\n"); } - ui_print("\nInstall from sdcard complete.\n"); ui_set_background(BACKGROUND_ICON_NONE); + ui_print("\nInstall from sdcard complete.\n"); } char* INSTALL_MENU_ITEMS[] = { "apply sdcard:update.zip", diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk index bbdc72f..790b8bd 100644 --- a/mtdutils/Android.mk +++ b/mtdutils/Android.mk @@ -30,28 +30,14 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SRC_FILES := flash_image.c -#LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE := recovery_flash_image -#LOCAL_MODULE_TAGS := eng -#LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc -#LOCAL_MODULE_STEM := flash_image -#LOCAL_FORCE_STATIC_EXECUTABLE := true -#LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE := libflash_image LOCAL_CFLAGS += -Dmain=flash_image_main -#ADDITIONAL_RECOVERY_EXECUTABLES += recovery_flash_image include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := dump_image.c -#LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE := recovery_dump_image -#LOCAL_MODULE_TAGS := eng -#LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc -#LOCAL_MODULE_STEM := dump_image -#LOCAL_FORCE_STATIC_EXECUTABLE := true -#LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE := libdump_image LOCAL_CFLAGS += -Dmain=dump_image_main -#ADDITIONAL_RECOVERY_EXECUTABLES += recovery_dump_image include $(BUILD_STATIC_LIBRARY) @@ -60,12 +46,12 @@ LOCAL_SRC_FILES := driver.c LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE := recovery_tools LOCAL_MODULE_TAGS := eng -LOCAL_STATIC_LIBRARIES := recovery_mkyaffs2image recovery_unyaffs recovery_dump_image recovery_flash_image libmtdutils libcutils libc +LOCAL_STATIC_LIBRARIES := libmkyaffs2image libunyaffs libdump_image libflash_image libmtdutils libcutils libc LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin LOCAL_UNSTRIPPED_PATH := $(TARGET_OUT_EXECUTABLES_UNSTRIPPED) ADDITIONAL_RECOVERY_EXECUTABLES += recovery_tools -include $(BUILD_EXECUTABLE) +#include $(BUILD_EXECUTABLE) endif # TARGET_ARCH == arm endif # !TARGET_SIMULATOR diff --git a/recovery.c b/recovery.c index 4dd8833..cc324cd 100644 --- a/recovery.c +++ b/recovery.c @@ -472,6 +472,18 @@ print_property(const char *key, const char *name, void *cookie) int main(int argc, char **argv) { + if (strstr(argv[0], "recovery") == NULL) + { + if (strstr(argv[0], "flash_image") != NULL) + return flash_image_main(argc, argv); + if (strstr(argv[0], "dump_image") != NULL) + return dump_image_main(argc, argv); + if (strstr(argv[0], "mkyaffs2image") != NULL) + return mkyaffs2image_main(argc, argv); + if (strstr(argv[0], "unyaffs") != NULL) + return unyaffs_main(argc, argv); + return busybox_driver(argc, argv); + } int is_user_initiated_recovery = 0; time_t start = time(NULL); From 73098ab18a065aff83e4bc899be3e9b001a7a3e4 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Sat, 6 Mar 2010 16:37:01 -0800 Subject: [PATCH 042/233] version 1.4.2. minor bug fixes. --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index c92facd..dd0a9cd 100644 --- a/Android.mk +++ b/Android.mk @@ -25,7 +25,7 @@ LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true -RECOVERY_API_VERSION := 1.4.1 +RECOVERY_API_VERSION := 1.4.2 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) # This binary is in the recovery ramdisk, which is otherwise a copy of root. From 2bda3e9fa09ccc51e3d5c10134bf91b41a4ab43f Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Sat, 6 Mar 2010 16:40:52 -0800 Subject: [PATCH 043/233] disable script asserts by default for user usage --- recovery.c | 1 + 1 file changed, 1 insertion(+) diff --git a/recovery.c b/recovery.c index cc324cd..ff81752 100644 --- a/recovery.c +++ b/recovery.c @@ -547,6 +547,7 @@ main(int argc, char **argv) // we are starting up in user initiated recovery here // let's set up some default options signature_check_enabled = 0; + script_assert_enabled = 0; is_user_initiated_recovery = 1; ui_set_show_text(1); } From f68aaaf20c1f8f20825d51d6e984e9f9d75e4393 Mon Sep 17 00:00:00 2001 From: "Koushik K. Dutta" Date: Sun, 7 Mar 2010 13:39:21 -0800 Subject: [PATCH 044/233] run amend scripts from the command line --- extendedcommands.c | 56 +++++++++++++++++++++++++++++++++++++++++++++ mtdutils/Android.mk | 25 ++++++++++---------- recovery.c | 2 ++ 3 files changed, 70 insertions(+), 13 deletions(-) diff --git a/extendedcommands.c b/extendedcommands.c index 3f52947..23d4094 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -30,6 +30,9 @@ #include "roots.h" #include "recovery_ui.h" +#include "commands.h" +#include "amend/amend.h" + int signature_check_enabled = 1; int script_assert_enabled = 1; static const char *SDCARD_PACKAGE_FILE = "SDCARD:update.zip"; @@ -383,3 +386,56 @@ void do_mount_usb_storage() system("echo '' > /sys/devices/platform/usb_mass_storage/lun0/file"); system("echo 0 > /sys/devices/platform/usb_mass_storage/lun0/enable"); } + +int amend_main(int argc, char** argv) +{ + if (argc != 2) + { + printf("Usage: amend