/* * This file is part of MultiROM. * * MultiROM is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MultiROM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MultiROM. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "devices.h" #include "../lib/log.h" #include "../lib/util.h" #include "../lib/fstab.h" #include "../lib/inject.h" #include "../version.h" #include "adb.h" #include "../hooks.h" #include "encryption.h" #define EXEC_MASK (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) #define REALDATA "/realdata" #define MULTIROM_BIN "multirom" #define BUSYBOX_BIN "busybox" #define KEEP_REALDATA "/dev/.keep_realdata" // Not defined in android includes? #define MS_RELATIME (1<<21) static char path_multirom[64] = { 0 }; static int find_multirom(void) { int i; struct stat info; static const char *paths[] = { REALDATA"/media/0/multirom", // 4.2 REALDATA"/media/multirom", NULL, }; for(i = 0; paths[i]; ++i) { if(stat(paths[i], &info) < 0) continue; strcpy(path_multirom, paths[i]); return 0; } return -1; } static void run_multirom(void) { char path[256]; struct stat info; // busybox sprintf(path, "%s/%s", path_multirom, BUSYBOX_BIN); if (stat(path, &info) < 0) { ERROR("Could not find busybox: %s\n", path); return; } chmod(path, EXEC_MASK); // restart after crash sprintf(path, "%s/restart_after_crash", path_multirom); int restart = (stat(path, &info) >= 0); // multirom sprintf(path, "%s/%s", path_multirom, MULTIROM_BIN); if (stat(path, &info) < 0) { ERROR("Could not find multirom: %s\n", path); return; } chmod(path, EXEC_MASK); char *cmd[] = { path, NULL }; do { ERROR("Running multirom: %s\n",path); int res = run_cmd(cmd); if(res == 0) break; else ERROR("MultiROM exited with status code %d!\n", res); } while(restart); } static int try_mount_all_entries(struct fstab *fstab, struct fstab_part *first_data_p) { size_t i; struct fstab_part *p_itr = first_data_p; do { // Remove nosuid flag, because secondary ROMs have // su binaries on /data p_itr->mountflags &= ~(MS_NOSUID); if(mount(p_itr->device, REALDATA, p_itr->type, p_itr->mountflags, p_itr->options) >= 0) return 0; } while((p_itr = fstab_find_next_by_path(fstab, "/data", p_itr))); ERROR("Failed to mount /realdata with data from fstab, trying all filesystems\n"); const char *fs_types[] = { "ext4", "f2fs", "ext3", "ext2" }; const char *fs_opts [] = { "barrier=1,data=ordered,nomblk_io_submit,noauto_da_alloc,errors=panic", // ext4 "inline_xattr,flush_merge,errors=recover", // f2fs "", // ext3 "" // ext2 }; for(i = 0; i < ARRAY_SIZE(fs_types); ++i) { if(mount(first_data_p->device, REALDATA, fs_types[i], first_data_p->mountflags, fs_opts[i]) >= 0) { INFO("/realdata successfuly mounted with fs %s\n", fs_types[i]); return 0; } } return -1; } static int mount_and_run(struct fstab *fstab) { struct fstab_part *datap = fstab_find_first_by_path(fstab, "/data"); if(!datap) { ERROR("Failed to find /data partition in fstab\n"); return -1; } if(access(datap->device, R_OK) < 0) { INFO("Waiting for %s\n", datap->device); if(wait_for_file(datap->device, 5) < 0) { ERROR("Waiting too long for dev %s\n", datap->device); return -1; } } mkdir(REALDATA, 0755); if(try_mount_all_entries(fstab, datap) < 0) { #ifndef MR_ENCRYPTION ERROR("Failed to mount /data with all possible filesystems!\n"); return -1; #else INFO("Failed to mount /data, trying encryption...\n"); switch(encryption_before_mount(fstab)) { case ENC_RES_ERR: ERROR("/data decryption failed!\n"); return -1; case ENC_RES_BOOT_INTERNAL: return 0; default: case ENC_RES_OK: { if(try_mount_all_entries(fstab, datap) < 0) { ERROR("Failed to mount decrypted /data with all possible filesystems!\n"); return -1; } break; } } #endif } if(find_multirom() == -1) { ERROR("Could not find multirom folder!\n"); return -1; } adb_init(path_multirom); run_multirom(); adb_quit(); return 0; } static int is_charger_mode(void) { char buff[2048] = { 0 }; FILE *f = fopen("/proc/cmdline", "re"); if(!f) return 0; fgets(buff, sizeof(buff), f); fclose(f); return (strstr(buff, "androidboot.mode=charger") != NULL); } static void fixup_symlinks(void) { static const char *init_links[] = { "/sbin/ueventd", "/sbin/watchdogd" }; size_t i; ssize_t len; char buff[64]; struct stat info; for(i = 0; i < ARRAY_SIZE(init_links); ++i) { if(lstat(init_links[i], &info) < 0 || !S_ISLNK(info.st_mode)) continue; if (info.st_size < sizeof(buff)-1) { len = readlink(init_links[i], buff, sizeof(buff)-1); if(len >= 0) { buff[len] = 0; // if the symlink already points to ../init, skip it. if(strcmp(buff, "../init") == 0) continue; } } ERROR("Fixing up symlink '%s' -> '%s' to '%s' -> '../init')\n", init_links[i], buff, init_links[i]); unlink(init_links[i]); symlink("../init", init_links[i]); } } int main(int argc, char *argv[]) { int i, res; static char *const cmd[] = { "/init", NULL }; struct fstab *fstab = NULL; char *inject_path = NULL; char *mrom_dir = NULL; int force_inject = 0; for(i = 1; i < argc; ++i) { if(strcmp(argv[i], "-v") == 0) { printf("%d\n", VERSION_TRAMPOLINE); fflush(stdout); return 0; } else if(strstartswith(argv[i], "--inject=")) inject_path = argv[i] + strlen("--inject="); else if(strstartswith(argv[i], "--mrom_dir=")) mrom_dir = argv[i] + strlen("--mrom_dir="); else if(strcmp(argv[i], "-f") == 0) force_inject = 1; } if(inject_path) { if(!mrom_dir) { printf("--mrom_dir=[path to multirom's data dir] needs to be specified!\n"); fflush(stdout); return 1; } mrom_set_dir(mrom_dir); mrom_set_log_tag("trampoline_inject"); return inject_bootimg(inject_path, force_inject); } umask(000); // Init only the little we need, leave the rest for real init mkdir("/dev", 0755); mkdir("/dev/pts", 0755); mkdir("/dev/socket", 0755); mkdir("/proc", 0755); mkdir("/sys", 0755); mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"); mount("devpts", "/dev/pts", "devpts", 0, NULL); mount("proc", "/proc", "proc", 0, NULL); mount("sysfs", "/sys", "sysfs", 0, NULL); mount("pstore", "/sys/fs/pstore", "pstore", 0, NULL); klog_init(); // output all messages to dmesg, // but it is possible to filter out INFO messages klog_set_level(6); mrom_set_log_tag("trampoline"); INFO("Running trampoline v%d\n", VERSION_TRAMPOLINE); if(is_charger_mode()) { INFO("Charger mode detected, skipping multirom\n"); goto run_main_init; } #if MR_DEVICE_HOOKS >= 3 tramp_hook_before_device_init(); #endif INFO("Initializing devices...\n"); devices_init(); INFO("Done initializing\n"); if(wait_for_file("/dev/graphics/fb0", 5) < 0) { ERROR("Waiting too long for fb0"); goto exit; } fstab = fstab_auto_load(); if(!fstab) goto exit; #if 0 fstab_dump(fstab); //debug #endif // mount and run multirom from sdcard if(mount_and_run(fstab) < 0 && mrom_is_second_boot()) { ERROR("This is second boot and we couldn't mount /data, reboot!\n"); sync(); android_reboot(ANDROID_RB_RESTART, 0, 0); while(1) sleep(1); } exit: if(fstab) fstab_destroy(fstab); // close and destroy everything devices_close(); run_main_init: umount("/dev/pts"); rmdir("/dev/pts"); rmdir("/dev/socket"); if(access(KEEP_REALDATA, F_OK) < 0) { umount(REALDATA); umount("/dev"); rmdir(REALDATA); encryption_destroy(); } encryption_cleanup(); umount("/proc"); umount("/sys/fs/pstore"); umount("/sys"); INFO("Running main_init\n"); fixup_symlinks(); chmod("/main_init", EXEC_MASK); rename("/main_init", "/init"); res = execve(cmd[0], cmd, NULL); ERROR("execve returned %d %d %s\n", res, errno, strerror(errno)); return 0; }