6d0604bf34
This is checked at runtime to see if /boot is a mountable parition (i.e not mtd), if so it adds an entry into /etc/fstab. This will allow us to mount /boot from an edify script and push certain files without completely imaging the parition which can contain other files such as the bootloader (u-boot), and recovery kernel and ramdisks as is the case with the encore (NookColor) Test on NookColor and passion. Correct entry was added to the NC and not added to the passion as expected. Change-Id: I9850dee866b77653bf400bb5193905e55da3f25f
1130 lines
33 KiB
C
1130 lines
33 KiB
C
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <limits.h>
|
|
#include <linux/input.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/reboot.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <sys/wait.h>
|
|
#include <sys/limits.h>
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <signal.h>
|
|
#include <sys/wait.h>
|
|
|
|
#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"
|
|
|
|
#include "../../external/yaffs2/yaffs2/utils/mkyaffs2image.h"
|
|
#include "../../external/yaffs2/yaffs2/utils/unyaffs.h"
|
|
|
|
#include "extendedcommands.h"
|
|
#include "nandroid.h"
|
|
#include "mounts.h"
|
|
#include "flashutils/flashutils.h"
|
|
#include "edify/expr.h"
|
|
|
|
int signature_check_enabled = 1;
|
|
int script_assert_enabled = 1;
|
|
static const char *SDCARD_UPDATE_FILE = "/sdcard/update.zip";
|
|
|
|
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", script_assert_enabled ? "Enabled" : "Disabled");
|
|
}
|
|
|
|
int install_zip(const char* packagefilepath)
|
|
{
|
|
ui_print("\n-- Installing: %s\n", packagefilepath);
|
|
if (device_flash_type() == MTD) {
|
|
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");
|
|
return 1;
|
|
}
|
|
ui_set_background(BACKGROUND_ICON_NONE);
|
|
ui_print("\nInstall from sdcard complete.\n");
|
|
return 0;
|
|
}
|
|
|
|
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, 0);
|
|
switch (chosen_item)
|
|
{
|
|
case ITEM_ASSERTS:
|
|
toggle_script_asserts();
|
|
break;
|
|
case ITEM_SIG_CHECK:
|
|
toggle_signature_check();
|
|
break;
|
|
case ITEM_APPLY_SDCARD:
|
|
{
|
|
if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip"))
|
|
install_zip(SDCARD_UPDATE_FILE);
|
|
break;
|
|
}
|
|
case ITEM_CHOOSE_ZIP:
|
|
show_choose_zip_menu();
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void free_string_array(char** array)
|
|
{
|
|
if (array == NULL)
|
|
return;
|
|
char* cursor = array[0];
|
|
int i = 0;
|
|
while (cursor != NULL)
|
|
{
|
|
free(cursor);
|
|
cursor = array[++i];
|
|
}
|
|
free(array);
|
|
}
|
|
|
|
char** gather_files(const char* directory, const char* fileExtensionOrDirectory, int* numFiles)
|
|
{
|
|
char path[PATH_MAX] = "";
|
|
DIR *dir;
|
|
struct dirent *de;
|
|
int total = 0;
|
|
int i;
|
|
char** files = NULL;
|
|
int pass;
|
|
*numFiles = 0;
|
|
int dirLen = strlen(directory);
|
|
|
|
dir = opendir(directory);
|
|
if (dir == NULL) {
|
|
ui_print("Couldn't open directory.\n");
|
|
return NULL;
|
|
}
|
|
|
|
int extension_length = 0;
|
|
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[PATH_MAX];
|
|
strcpy(fullFileName, directory);
|
|
strcat(fullFileName, de->d_name);
|
|
stat(fullFileName, &info);
|
|
// 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) {
|
|
return NULL;
|
|
}
|
|
|
|
// sort the result
|
|
if (files != NULL) {
|
|
for (i = 0; i < total; i++) {
|
|
int curMax = -1;
|
|
int j;
|
|
for (j = 0; j < total - i; j++) {
|
|
if (curMax == -1 || strcmp(files[curMax], files[j]) < 0)
|
|
curMax = j;
|
|
}
|
|
char* temp = files[curMax];
|
|
files[curMax] = files[total - i - 1];
|
|
files[total - i - 1] = temp;
|
|
}
|
|
}
|
|
|
|
return files;
|
|
}
|
|
|
|
// 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;
|
|
char* return_value = NULL;
|
|
int dir_len = strlen(directory);
|
|
|
|
char** files = gather_files(directory, fileExtensionOrDirectory, &numFiles);
|
|
char** dirs = NULL;
|
|
if (fileExtensionOrDirectory != NULL)
|
|
dirs = gather_files(directory, NULL, &numDirs);
|
|
int total = numDirs + numFiles;
|
|
if (total == 0)
|
|
{
|
|
ui_print("No files found.\n");
|
|
}
|
|
else
|
|
{
|
|
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 < numFiles; i++)
|
|
{
|
|
list[numDirs + i] = strdup(files[i] + dir_len);
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
int chosen_item = get_menu_selection(headers, list, 0, 0);
|
|
if (chosen_item == GO_BACK)
|
|
break;
|
|
static char ret[PATH_MAX];
|
|
if (chosen_item < numDirs)
|
|
{
|
|
char* subret = choose_file_menu(dirs[chosen_item], fileExtensionOrDirectory, headers);
|
|
if (subret != NULL)
|
|
{
|
|
strcpy(ret, subret);
|
|
return_value = ret;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
strcpy(ret, files[chosen_item - numDirs]);
|
|
return_value = ret;
|
|
break;
|
|
}
|
|
free_string_array(list);
|
|
}
|
|
|
|
free_string_array(files);
|
|
free_string_array(dirs);
|
|
return return_value;
|
|
}
|
|
|
|
void show_choose_zip_menu()
|
|
{
|
|
if (ensure_path_mounted("/sdcard") != 0) {
|
|
LOGE ("Can't mount /sdcard\n");
|
|
return;
|
|
}
|
|
|
|
static char* headers[] = { "Choose a zip to apply",
|
|
"",
|
|
NULL
|
|
};
|
|
|
|
char* file = choose_file_menu("/sdcard/", ".zip", headers);
|
|
if (file == NULL)
|
|
return;
|
|
static char* confirm_install = "Confirm install?";
|
|
static char confirm[PATH_MAX];
|
|
sprintf(confirm, "Yes - Install %s", basename(file));
|
|
if (confirm_selection(confirm_install, confirm))
|
|
install_zip(file);
|
|
}
|
|
|
|
void show_nandroid_restore_menu()
|
|
{
|
|
if (ensure_path_mounted("/sdcard") != 0) {
|
|
LOGE ("Can't mount /sdcard\n");
|
|
return;
|
|
}
|
|
|
|
static char* headers[] = { "Choose an image to restore",
|
|
"",
|
|
NULL
|
|
};
|
|
|
|
char* file = choose_file_menu("/sdcard/clockworkmod/backup/", NULL, headers);
|
|
if (file == NULL)
|
|
return;
|
|
|
|
if (confirm_selection("Confirm restore?", "Yes - Restore"))
|
|
nandroid_restore(file, 1, 1, 1, 1, 1, 0);
|
|
}
|
|
|
|
void show_mount_usb_storage_menu()
|
|
{
|
|
int fd;
|
|
Volume *vol = volume_for_path("/sdcard");
|
|
if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file",
|
|
O_WRONLY)) < 0) {
|
|
LOGE("Unable to open ums lunfile (%s)", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (write(fd, vol->device, strlen(vol->device)) < 0) {
|
|
LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
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, 0);
|
|
if (chosen_item == GO_BACK || chosen_item == 0)
|
|
break;
|
|
}
|
|
|
|
if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file", O_WRONLY)) < 0) {
|
|
LOGE("Unable to open ums lunfile (%s)", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
char ch = 0;
|
|
if (write(fd, &ch, 1) < 0) {
|
|
LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int confirm_selection(const char* title, const char* confirm)
|
|
{
|
|
struct stat info;
|
|
if (0 == stat("/sdcard/clockworkmod/.no_confirm", &info))
|
|
return 1;
|
|
|
|
char* confirm_headers[] = { title, " THIS CAN NOT BE UNDONE.", "", NULL };
|
|
char* items[] = { "No",
|
|
"No",
|
|
"No",
|
|
"No",
|
|
"No",
|
|
"No",
|
|
"No",
|
|
confirm, //" Yes -- wipe partition", // [7
|
|
"No",
|
|
"No",
|
|
"No",
|
|
NULL };
|
|
|
|
int chosen_item = get_menu_selection(confirm_headers, items, 0, 0);
|
|
return chosen_item == 7;
|
|
}
|
|
|
|
#define MKE2FS_BIN "/sbin/mke2fs"
|
|
#define TUNE2FS_BIN "/sbin/tune2fs"
|
|
#define E2FSCK_BIN "/sbin/e2fsck"
|
|
|
|
int format_unknown_device(const char *device, const char* path, const char *fs_type)
|
|
{
|
|
LOGI("Formatting unknown device.\n");
|
|
|
|
// device may simply be a name, like "system"
|
|
if (device[0] != '/')
|
|
return erase_raw_partition(device);
|
|
|
|
// if this is SDEXT:, don't worry about it if it does not exist.
|
|
if (0 == strcmp(path, "/sd-ext"))
|
|
{
|
|
struct stat st;
|
|
Volume *vol = volume_for_path("/sd-ext");
|
|
if (vol == NULL || 0 != stat(vol->device, &st))
|
|
{
|
|
ui_print("No app2sd partition found. Skipping format of /sd-ext.\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (NULL != fs_type) {
|
|
if (strcmp("ext3", fs_type) == 0) {
|
|
LOGI("Formatting ext3 device.\n");
|
|
if (0 != ensure_path_unmounted(path)) {
|
|
LOGE("Error while unmounting %s.\n", path);
|
|
return -12;
|
|
}
|
|
return format_ext3_device(device);
|
|
}
|
|
|
|
if (strcmp("ext2", fs_type) == 0) {
|
|
LOGI("Formatting ext2 device.\n");
|
|
if (0 != ensure_path_unmounted(path)) {
|
|
LOGE("Error while unmounting %s.\n", path);
|
|
return -12;
|
|
}
|
|
return format_ext2_device(device);
|
|
}
|
|
}
|
|
|
|
if (0 != ensure_path_mounted(path))
|
|
{
|
|
ui_print("Error mounting %s!\n", path);
|
|
ui_print("Skipping format...\n");
|
|
return 0;
|
|
}
|
|
|
|
static char tmp[PATH_MAX];
|
|
sprintf(tmp, "rm -rf %s/*", path);
|
|
__system(tmp);
|
|
sprintf(tmp, "rm -rf %s/.*", path);
|
|
__system(tmp);
|
|
|
|
ensure_path_unmounted(path);
|
|
return 0;
|
|
}
|
|
|
|
//#define MOUNTABLE_COUNT 5
|
|
//#define DEVICE_COUNT 4
|
|
//#define MMC_COUNT 2
|
|
|
|
void show_partition_menu()
|
|
{
|
|
static char* headers[] = { "Mounts and Storage Menu",
|
|
"",
|
|
NULL
|
|
};
|
|
|
|
typedef char* string;
|
|
string mounts[][3] = {
|
|
{ "mount /system", "unmount /system", "/system" },
|
|
{ "mount /data", "unmount /data", "/data" },
|
|
{ "mount /cache", "unmount /cache", "/cache" },
|
|
{ "mount /sdcard", "unmount /sdcard", "/sdcard" },
|
|
#ifdef BOARD_HAS_SDCARD_INTERNAL
|
|
{ "mount /emmc", "unmount /emmc", "/emmc" },
|
|
#endif
|
|
{ "mount /sd-ext", "unmount /sd-ext", "/sd-ext" }
|
|
};
|
|
|
|
string devices[][2] = {
|
|
{ "format boot", "/boot" },
|
|
{ "format system", "/system" },
|
|
{ "format data", "/data" },
|
|
{ "format cache", "/cache" },
|
|
{ "format sdcard", "/sdcard" },
|
|
{ "format sd-ext", "/sd-ext" },
|
|
#ifdef BOARD_HAS_SDCARD_INTERNAL
|
|
{ "format internal sdcard", "/emmc" }
|
|
#endif
|
|
};
|
|
|
|
const int MOUNTABLE_COUNT = sizeof(mounts) / sizeof(string) / 3;
|
|
const int DEVICE_COUNT = sizeof(devices) / sizeof(string) / 2;
|
|
|
|
static char* confirm_format = "Confirm format?";
|
|
static char* confirm = "Yes - Format";
|
|
|
|
for (;;)
|
|
{
|
|
int ismounted[MOUNTABLE_COUNT];
|
|
int i;
|
|
static string options[MOUNTABLE_COUNT + DEVICE_COUNT + 1 + 1]; // mountables, format mtds, format mmcs, usb storage, null
|
|
for (i = 0; i < MOUNTABLE_COUNT; i++)
|
|
{
|
|
ismounted[i] = is_path_mounted(mounts[i][2]);
|
|
options[i] = ismounted[i] ? mounts[i][1] : mounts[i][0];
|
|
}
|
|
|
|
for (i = 0; i < DEVICE_COUNT; i++)
|
|
{
|
|
options[MOUNTABLE_COUNT + i] = devices[i][0];
|
|
}
|
|
|
|
options[MOUNTABLE_COUNT + DEVICE_COUNT] = "mount USB storage";
|
|
options[MOUNTABLE_COUNT + DEVICE_COUNT + 1] = NULL;
|
|
|
|
int chosen_item = get_menu_selection(headers, options, 0, 0);
|
|
if (chosen_item == GO_BACK)
|
|
break;
|
|
if (chosen_item == MOUNTABLE_COUNT + DEVICE_COUNT)
|
|
{
|
|
show_mount_usb_storage_menu();
|
|
}
|
|
else if (chosen_item < MOUNTABLE_COUNT)
|
|
{
|
|
if (ismounted[chosen_item])
|
|
{
|
|
if (0 != ensure_path_unmounted(mounts[chosen_item][2]))
|
|
ui_print("Error unmounting %s!\n", mounts[chosen_item][2]);
|
|
}
|
|
else
|
|
{
|
|
if (0 != ensure_path_mounted(mounts[chosen_item][2]))
|
|
ui_print("Error mounting %s!\n", mounts[chosen_item][2]);
|
|
}
|
|
}
|
|
else if (chosen_item < MOUNTABLE_COUNT + DEVICE_COUNT)
|
|
{
|
|
chosen_item = chosen_item - MOUNTABLE_COUNT;
|
|
if (!confirm_selection(confirm_format, confirm))
|
|
continue;
|
|
ui_print("Formatting %s...\n", devices[chosen_item][1]);
|
|
if (0 != format_volume(devices[chosen_item][1]))
|
|
ui_print("Error formatting %s!\n", devices[chosen_item][1]);
|
|
else
|
|
ui_print("Done.\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
#define EXTENDEDCOMMAND_SCRIPT "/cache/recovery/extendedcommand"
|
|
|
|
int extendedcommand_file_exists()
|
|
{
|
|
struct stat file_info;
|
|
return 0 == stat(EXTENDEDCOMMAND_SCRIPT, &file_info);
|
|
}
|
|
|
|
int edify_main(int argc, char** argv) {
|
|
load_volume_table();
|
|
process_volumes();
|
|
RegisterBuiltins();
|
|
RegisterRecoveryHooks();
|
|
FinishRegistration();
|
|
|
|
if (argc != 2) {
|
|
printf("edify <filename>\n");
|
|
return 1;
|
|
}
|
|
|
|
FILE* f = fopen(argv[1], "r");
|
|
if (f == NULL) {
|
|
printf("%s: %s: No such file or directory\n", argv[0], argv[1]);
|
|
return 1;
|
|
}
|
|
char buffer[8192];
|
|
int size = fread(buffer, 1, 8191, f);
|
|
fclose(f);
|
|
buffer[size] = '\0';
|
|
|
|
Expr* root;
|
|
int error_count = 0;
|
|
yy_scan_bytes(buffer, size);
|
|
int error = yyparse(&root, &error_count);
|
|
printf("parse returned %d; %d errors encountered\n", error, error_count);
|
|
if (error == 0 || error_count > 0) {
|
|
|
|
//ExprDump(0, root, buffer);
|
|
|
|
State state;
|
|
state.cookie = NULL;
|
|
state.script = buffer;
|
|
state.errmsg = NULL;
|
|
|
|
char* result = Evaluate(&state, root);
|
|
if (result == NULL) {
|
|
printf("result was NULL, message is: %s\n",
|
|
(state.errmsg == NULL ? "(NULL)" : state.errmsg));
|
|
free(state.errmsg);
|
|
} else {
|
|
printf("result is [%s]\n", result);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int run_script(char* filename)
|
|
{
|
|
struct stat file_info;
|
|
if (0 != stat(filename, &file_info)) {
|
|
printf("Error executing stat on file: %s\n", filename);
|
|
return 1;
|
|
}
|
|
|
|
int script_len = file_info.st_size;
|
|
char* script_data = (char*)malloc(script_len + 1);
|
|
FILE *file = fopen(filename, "rb");
|
|
fread(script_data, script_len, 1, file);
|
|
// supposedly not necessary, but let's be safe.
|
|
script_data[script_len] = '\0';
|
|
fclose(file);
|
|
LOGI("Running script:\n");
|
|
LOGI("\n%s\n", script_data);
|
|
|
|
int ret = run_script_from_buffer(script_data, script_len, filename);
|
|
free(script_data);
|
|
return ret;
|
|
}
|
|
|
|
int run_and_remove_extendedcommand()
|
|
{
|
|
char tmp[PATH_MAX];
|
|
sprintf(tmp, "cp %s /tmp/%s", EXTENDEDCOMMAND_SCRIPT, basename(EXTENDEDCOMMAND_SCRIPT));
|
|
__system(tmp);
|
|
remove(EXTENDEDCOMMAND_SCRIPT);
|
|
int i = 0;
|
|
for (i = 20; i > 0; i--) {
|
|
ui_print("Waiting for SD Card to mount (%ds)\n", i);
|
|
if (ensure_path_mounted("/sdcard") == 0) {
|
|
ui_print("SD Card mounted...\n");
|
|
break;
|
|
}
|
|
sleep(1);
|
|
}
|
|
remove("/sdcard/clockworkmod/.recoverycheckpoint");
|
|
if (i == 0) {
|
|
ui_print("Timed out waiting for SD card... continuing anyways.");
|
|
}
|
|
|
|
sprintf(tmp, "/tmp/%s", basename(EXTENDEDCOMMAND_SCRIPT));
|
|
return run_script(tmp);
|
|
}
|
|
|
|
void show_nandroid_advanced_restore_menu()
|
|
{
|
|
if (ensure_path_mounted("/sdcard") != 0) {
|
|
LOGE ("Can't mount /sdcard\n");
|
|
return;
|
|
}
|
|
|
|
static char* advancedheaders[] = { "Choose an image to restore",
|
|
"",
|
|
"Choose an image to restore",
|
|
"first. The next menu will",
|
|
"you more options.",
|
|
"",
|
|
NULL
|
|
};
|
|
|
|
char* file = choose_file_menu("/sdcard/clockworkmod/backup/", NULL, advancedheaders);
|
|
if (file == NULL)
|
|
return;
|
|
|
|
static char* headers[] = { "Nandroid Advanced Restore",
|
|
"",
|
|
NULL
|
|
};
|
|
|
|
static char* list[] = { "Restore boot",
|
|
"Restore system",
|
|
"Restore data",
|
|
"Restore cache",
|
|
"Restore sd-ext",
|
|
"Restore wimax",
|
|
NULL
|
|
};
|
|
|
|
char tmp[PATH_MAX];
|
|
if (0 != get_partition_device("wimax", tmp)) {
|
|
// disable wimax restore option
|
|
list[5] = NULL;
|
|
}
|
|
|
|
static char* confirm_restore = "Confirm restore?";
|
|
|
|
int chosen_item = get_menu_selection(headers, list, 0, 0);
|
|
switch (chosen_item)
|
|
{
|
|
case 0:
|
|
if (confirm_selection(confirm_restore, "Yes - Restore boot"))
|
|
nandroid_restore(file, 1, 0, 0, 0, 0, 0);
|
|
break;
|
|
case 1:
|
|
if (confirm_selection(confirm_restore, "Yes - Restore system"))
|
|
nandroid_restore(file, 0, 1, 0, 0, 0, 0);
|
|
break;
|
|
case 2:
|
|
if (confirm_selection(confirm_restore, "Yes - Restore data"))
|
|
nandroid_restore(file, 0, 0, 1, 0, 0, 0);
|
|
break;
|
|
case 3:
|
|
if (confirm_selection(confirm_restore, "Yes - Restore cache"))
|
|
nandroid_restore(file, 0, 0, 0, 1, 0, 0);
|
|
break;
|
|
case 4:
|
|
if (confirm_selection(confirm_restore, "Yes - Restore sd-ext"))
|
|
nandroid_restore(file, 0, 0, 0, 0, 1, 0);
|
|
break;
|
|
case 5:
|
|
if (confirm_selection(confirm_restore, "Yes - Restore wimax"))
|
|
nandroid_restore(file, 0, 0, 0, 0, 0, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void show_nandroid_menu()
|
|
{
|
|
static char* headers[] = { "Nandroid",
|
|
"",
|
|
NULL
|
|
};
|
|
|
|
static char* list[] = { "Backup",
|
|
"Restore",
|
|
"Advanced Restore",
|
|
NULL
|
|
};
|
|
|
|
int chosen_item = get_menu_selection(headers, list, 0, 0);
|
|
switch (chosen_item)
|
|
{
|
|
case 0:
|
|
{
|
|
char backup_path[PATH_MAX];
|
|
time_t t = time(NULL);
|
|
struct tm *tmp = localtime(&t);
|
|
if (tmp == NULL)
|
|
{
|
|
struct timeval tp;
|
|
gettimeofday(&tp, NULL);
|
|
sprintf(backup_path, "/sdcard/clockworkmod/backup/%d", tp.tv_sec);
|
|
}
|
|
else
|
|
{
|
|
strftime(backup_path, sizeof(backup_path), "/sdcard/clockworkmod/backup/%F.%H.%M.%S", tmp);
|
|
}
|
|
nandroid_backup(backup_path);
|
|
}
|
|
break;
|
|
case 1:
|
|
show_nandroid_restore_menu();
|
|
break;
|
|
case 2:
|
|
show_nandroid_advanced_restore_menu();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void wipe_battery_stats()
|
|
{
|
|
ensure_path_mounted("/data");
|
|
remove("/data/system/batterystats.bin");
|
|
ensure_path_unmounted("/data");
|
|
}
|
|
|
|
void show_advanced_menu()
|
|
{
|
|
static char* headers[] = { "Advanced and Debugging Menu",
|
|
"",
|
|
NULL
|
|
};
|
|
|
|
static char* list[] = { "Reboot Recovery",
|
|
"Wipe Dalvik Cache",
|
|
"Wipe Battery Stats",
|
|
"Report Error",
|
|
"Key Test",
|
|
#ifndef BOARD_HAS_SMALL_RECOVERY
|
|
"Partition SD Card",
|
|
"Fix Permissions",
|
|
#ifdef BOARD_HAS_SDCARD_INTERNAL
|
|
"Partition Internal SD Card",
|
|
#endif
|
|
#endif
|
|
NULL
|
|
};
|
|
|
|
for (;;)
|
|
{
|
|
int chosen_item = get_menu_selection(headers, list, 0, 0);
|
|
if (chosen_item == GO_BACK)
|
|
break;
|
|
switch (chosen_item)
|
|
{
|
|
case 0:
|
|
__reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, "recovery");
|
|
break;
|
|
case 1:
|
|
{
|
|
if (0 != ensure_path_mounted("/data"))
|
|
break;
|
|
ensure_path_mounted("/sd-ext");
|
|
ensure_path_mounted("/cache");
|
|
if (confirm_selection( "Confirm wipe?", "Yes - Wipe Dalvik Cache")) {
|
|
__system("rm -r /data/dalvik-cache");
|
|
__system("rm -r /cache/dalvik-cache");
|
|
__system("rm -r /sd-ext/dalvik-cache");
|
|
}
|
|
ensure_path_unmounted("/data");
|
|
ui_print("Dalvik Cache wiped.\n");
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
if (confirm_selection( "Confirm wipe?", "Yes - Wipe Battery Stats"))
|
|
wipe_battery_stats();
|
|
break;
|
|
}
|
|
case 3:
|
|
handle_failure(1);
|
|
break;
|
|
case 4:
|
|
{
|
|
ui_print("Outputting key codes.\n");
|
|
ui_print("Go back to end debugging.\n");
|
|
int key;
|
|
int action;
|
|
do
|
|
{
|
|
key = ui_wait_key();
|
|
action = device_handle_key(key, 1);
|
|
ui_print("Key: %d\n", key);
|
|
}
|
|
while (action != GO_BACK);
|
|
break;
|
|
}
|
|
case 5:
|
|
{
|
|
static char* ext_sizes[] = { "128M",
|
|
"256M",
|
|
"512M",
|
|
"1024M",
|
|
"2048M",
|
|
"4096M",
|
|
NULL };
|
|
|
|
static char* swap_sizes[] = { "0M",
|
|
"32M",
|
|
"64M",
|
|
"128M",
|
|
"256M",
|
|
NULL };
|
|
|
|
static char* ext_headers[] = { "Ext Size", "", NULL };
|
|
static char* swap_headers[] = { "Swap Size", "", NULL };
|
|
|
|
int ext_size = get_menu_selection(ext_headers, ext_sizes, 0, 0);
|
|
if (ext_size == GO_BACK)
|
|
continue;
|
|
|
|
int swap_size = get_menu_selection(swap_headers, swap_sizes, 0, 0);
|
|
if (swap_size == GO_BACK)
|
|
continue;
|
|
|
|
char sddevice[256];
|
|
Volume *vol = volume_for_path("/sdcard");
|
|
strcpy(sddevice, vol->device);
|
|
// we only want the mmcblk, not the partition
|
|
sddevice[strlen("/dev/block/mmcblkX")] = NULL;
|
|
char cmd[PATH_MAX];
|
|
setenv("SDPATH", sddevice, 1);
|
|
sprintf(cmd, "sdparted -es %s -ss %s -efs ext3 -s", ext_sizes[ext_size], swap_sizes[swap_size]);
|
|
ui_print("Partitioning SD Card... please wait...\n");
|
|
if (0 == __system(cmd))
|
|
ui_print("Done!\n");
|
|
else
|
|
ui_print("An error occured while partitioning your SD Card. Please see /tmp/recovery.log for more details.\n");
|
|
break;
|
|
}
|
|
case 6:
|
|
{
|
|
ensure_path_mounted("/system");
|
|
ensure_path_mounted("/data");
|
|
ui_print("Fixing permissions...\n");
|
|
__system("fix_permissions");
|
|
ui_print("Done!\n");
|
|
break;
|
|
}
|
|
case 7:
|
|
{
|
|
static char* ext_sizes[] = { "128M",
|
|
"256M",
|
|
"512M",
|
|
"1024M",
|
|
"2048M",
|
|
"4096M",
|
|
NULL };
|
|
|
|
static char* swap_sizes[] = { "0M",
|
|
"32M",
|
|
"64M",
|
|
"128M",
|
|
"256M",
|
|
NULL };
|
|
|
|
static char* ext_headers[] = { "Data Size", "", NULL };
|
|
static char* swap_headers[] = { "Swap Size", "", NULL };
|
|
|
|
int ext_size = get_menu_selection(ext_headers, ext_sizes, 0, 0);
|
|
if (ext_size == GO_BACK)
|
|
continue;
|
|
|
|
int swap_size = 0;
|
|
if (swap_size == GO_BACK)
|
|
continue;
|
|
|
|
char sddevice[256];
|
|
Volume *vol = volume_for_path("/emmc");
|
|
strcpy(sddevice, vol->device);
|
|
// we only want the mmcblk, not the partition
|
|
sddevice[strlen("/dev/block/mmcblkX")] = NULL;
|
|
char cmd[PATH_MAX];
|
|
setenv("SDPATH", sddevice, 1);
|
|
sprintf(cmd, "sdparted -es %s -ss %s -efs ext3 -s", ext_sizes[ext_size], swap_sizes[swap_size]);
|
|
ui_print("Partitioning Internal SD Card... please wait...\n");
|
|
if (0 == __system(cmd))
|
|
ui_print("Done!\n");
|
|
else
|
|
ui_print("An error occured while partitioning your Internal SD Card. Please see /tmp/recovery.log for more details.\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void write_fstab_root(char *path, FILE *file)
|
|
{
|
|
Volume *vol = volume_for_path(path);
|
|
if (vol == NULL) {
|
|
LOGW("Unable to get recovery.fstab info for %s during fstab generation!\n", path);
|
|
return;
|
|
}
|
|
|
|
char device[200];
|
|
if (vol->device[0] != '/')
|
|
get_partition_device(vol->device, device);
|
|
else
|
|
strcpy(device, vol->device);
|
|
|
|
fprintf(file, "%s ", device);
|
|
fprintf(file, "%s ", path);
|
|
fprintf(file, "%s rw\n", vol->fs_type);
|
|
}
|
|
|
|
void create_fstab()
|
|
{
|
|
struct stat info;
|
|
__system("touch /etc/mtab");
|
|
FILE *file = fopen("/etc/fstab", "w");
|
|
if (file == NULL) {
|
|
LOGW("Unable to create /etc/fstab!\n");
|
|
return;
|
|
}
|
|
Volume *vol = volume_for_path("/boot");
|
|
if (NULL != vol && strcmp(vol->fs_type, "mtd") != 0)
|
|
write_fstab_root("/boot", file);
|
|
write_fstab_root("/cache", file);
|
|
write_fstab_root("/data", file);
|
|
if (has_datadata()) {
|
|
write_fstab_root("/datadata", file);
|
|
}
|
|
write_fstab_root("/system", file);
|
|
write_fstab_root("/sdcard", file);
|
|
write_fstab_root("/sd-ext", file);
|
|
fclose(file);
|
|
LOGI("Completed outputting fstab.\n");
|
|
}
|
|
|
|
int bml_check_volume(const char *path) {
|
|
ui_print("Checking %s...\n", path);
|
|
ensure_path_unmounted(path);
|
|
if (0 == ensure_path_mounted(path)) {
|
|
ensure_path_unmounted(path);
|
|
return 0;
|
|
}
|
|
|
|
Volume *vol = volume_for_path(path);
|
|
if (vol == NULL) {
|
|
LOGE("Unable process volume! Skipping...\n");
|
|
return 0;
|
|
}
|
|
|
|
ui_print("%s may be rfs. Checking...\n", path);
|
|
char tmp[PATH_MAX];
|
|
sprintf(tmp, "mount -t rfs %s %s", vol->device, path);
|
|
int ret = __system(tmp);
|
|
printf("%d\n", ret);
|
|
return ret == 0 ? 1 : 0;
|
|
}
|
|
|
|
void process_volumes() {
|
|
create_fstab();
|
|
|
|
if (device_flash_type() != BML)
|
|
return;
|
|
|
|
ui_print("Checking for ext4 partitions...\n");
|
|
int ret = 0;
|
|
ret = bml_check_volume("/system");
|
|
ret |= bml_check_volume("/data");
|
|
if (has_datadata())
|
|
ret |= bml_check_volume("/datadata");
|
|
ret |= bml_check_volume("/cache");
|
|
|
|
if (ret == 0) {
|
|
ui_print("Done!\n");
|
|
return;
|
|
}
|
|
|
|
char backup_path[PATH_MAX];
|
|
time_t t = time(NULL);
|
|
char backup_name[PATH_MAX];
|
|
struct timeval tp;
|
|
gettimeofday(&tp, NULL);
|
|
sprintf(backup_name, "before-ext4-convert-%d", tp.tv_sec);
|
|
sprintf(backup_path, "/sdcard/clockworkmod/backup/%s", backup_name);
|
|
|
|
ui_set_show_text(1);
|
|
ui_print("Filesystems need to be converted to ext4.\n");
|
|
ui_print("A backup and restore will now take place.\n");
|
|
ui_print("If anything goes wrong, your backup will be\n");
|
|
ui_print("named %s. Try restoring it\n", backup_name);
|
|
ui_print("in case of error.\n");
|
|
|
|
nandroid_backup(backup_path);
|
|
nandroid_restore(backup_path, 1, 1, 1, 1, 1, 0);
|
|
ui_set_show_text(0);
|
|
}
|
|
|
|
void handle_failure(int ret)
|
|
{
|
|
if (ret == 0)
|
|
return;
|
|
if (0 != ensure_path_mounted("/sdcard"))
|
|
return;
|
|
mkdir("/sdcard/clockworkmod", S_IRWXU);
|
|
__system("cp /tmp/recovery.log /sdcard/clockworkmod/recovery.log");
|
|
ui_print("/tmp/recovery.log was copied to /sdcard/clockworkmod/recovery.log. Please open ROM Manager to report the issue.\n");
|
|
}
|
|
|
|
int is_path_mounted(const char* path) {
|
|
Volume* v = volume_for_path(path);
|
|
if (v == NULL) {
|
|
return 0;
|
|
}
|
|
if (strcmp(v->fs_type, "ramdisk") == 0) {
|
|
// the ramdisk is always mounted.
|
|
return 1;
|
|
}
|
|
|
|
int result;
|
|
result = scan_mounted_volumes();
|
|
if (result < 0) {
|
|
LOGE("failed to scan mounted volumes\n");
|
|
return 0;
|
|
}
|
|
|
|
const MountedVolume* mv =
|
|
find_mounted_volume_by_mount_point(v->mount_point);
|
|
if (mv) {
|
|
// volume is already mounted
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int has_datadata() {
|
|
Volume *vol = volume_for_path("/datadata");
|
|
return vol != NULL;
|
|
}
|
|
|
|
int volume_main(int argc, char **argv) {
|
|
load_volume_table();
|
|
return 0;
|
|
}
|
|
|
|
void handle_chargemode() {
|
|
const char* filename = "/proc/cmdline";
|
|
struct stat file_info;
|
|
if (0 != stat(filename, &file_info))
|
|
return;
|
|
|
|
int file_len = file_info.st_size;
|
|
char* file_data = (char*)malloc(file_len + 1);
|
|
FILE *file = fopen(filename, "rb");
|
|
if (file == NULL)
|
|
return;
|
|
fread(file_data, file_len, 1, file);
|
|
// supposedly not necessary, but let's be safe.
|
|
file_data[file_len] = '\0';
|
|
fclose(file);
|
|
|
|
if (strstr(file_data, "androidboot.mode=offmode_charging") != NULL)
|
|
reboot(RB_POWER_OFF);
|
|
} |