Files
multirom_m86/trampoline/trampoline.c
Vojtech Bocek d3147e465c [t22] Don't move /main_init to /init before executing it
* This causes "Device or resource busy" when remounting rootfs as RO
  on hammerhead with encryption and thus causing bootloop because...
  why the fuck exactly? Only on this device and only with encryption?
  I don't care at this point, already took several hours to debug,
  hopefuly SELinux won't mind.
2015-03-11 02:17:08 +01:00

395 lines
9.7 KiB
C

/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <sys/stat.h>
#include <sys/mount.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <cutils/android_reboot.h>
#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\n");
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, "../main_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("../main_init", init_links[i]);
}
}
int main(int argc, char *argv[])
{
int i, res;
static char *const cmd[] = { "/main_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:
if(access(KEEP_REALDATA, F_OK) < 0)
{
umount(REALDATA);
umount("/dev/pts");
umount("/dev");
rmdir("/dev/pts");
rmdir("/dev/socket");
rmdir("/dev");
rmdir(REALDATA);
encryption_destroy();
}
encryption_cleanup();
umount("/proc");
umount("/sys/fs/pstore");
umount("/sys");
rmdir("/proc");
rmdir("/sys");
INFO("Running main_init\n");
fixup_symlinks();
chmod("/main_init", EXEC_MASK);
res = execve(cmd[0], cmd, NULL);
ERROR("execve returned %d %d %s\n", res, errno, strerror(errno));
return 0;
}