2019-05-27 09:29:43 +02:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
2019-07-16 10:08:28 +02:00
|
|
|
#include <vector>
|
2019-05-27 09:29:43 +02:00
|
|
|
|
2020-03-09 09:50:30 +01:00
|
|
|
#include <magisk.hpp>
|
|
|
|
#include <magiskpolicy.hpp>
|
|
|
|
#include <utils.hpp>
|
2019-05-27 09:29:43 +02:00
|
|
|
|
2020-03-09 09:50:30 +01:00
|
|
|
#include "init.hpp"
|
|
|
|
#include "magiskrc.inc"
|
2019-05-27 09:29:43 +02:00
|
|
|
|
|
|
|
#ifdef USE_64BIT
|
|
|
|
#define LIBNAME "lib64"
|
|
|
|
#else
|
|
|
|
#define LIBNAME "lib"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
static void patch_socket_name(const char *path) {
|
2019-07-15 02:41:51 +02:00
|
|
|
char *buf;
|
2019-05-27 09:29:43 +02:00
|
|
|
size_t size;
|
|
|
|
mmap_rw(path, buf, size);
|
|
|
|
for (int i = 0; i < size; ++i) {
|
|
|
|
if (memcmp(buf + i, MAIN_SOCKET, sizeof(MAIN_SOCKET)) == 0) {
|
2019-12-28 14:27:55 +01:00
|
|
|
gen_rand_str(buf + i, 16);
|
2019-07-15 02:41:51 +02:00
|
|
|
i += sizeof(MAIN_SOCKET);
|
2019-05-27 09:29:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
munmap(buf, size);
|
|
|
|
}
|
|
|
|
|
2019-07-16 10:08:28 +02:00
|
|
|
static vector<raw_data> rc_list;
|
|
|
|
|
2019-06-26 08:31:59 +02:00
|
|
|
static void patch_init_rc(FILE *rc) {
|
Logical Resizable Android Partitions support
The way how logical partition, or "Logical Resizable Android Partitions"
as they say in AOSP source code, is setup makes it impossible to early
mount the partitions from the shared super partition with just
a few lines of code; in fact, AOSP has a whole "fs_mgr" folder which
consist of multiple complex libraries, with 15K lines of code just
to deal with the device mapper shenanigans.
In order to keep the already overly complicated MagiskInit more
managable, I chose NOT to go the route of including fs_mgr directly
into MagiskInit. Luckily, starting from Android Q, Google decided to
split init startup into 3 stages, with the first stage doing _only_
early mount. This is great news, because we can simply let the stock
init do its own thing for us, and we intercept the bootup sequence.
So the workflow can be visualized roughly below:
Magisk First Stage --> First Stage Mount --> Magisk Second Stage --+
(MagiskInit) (Original Init) (MagiskInit) +
+
+
...Rest of the boot... <-- Second Stage <-- Selinux Setup <--+
(__________________ Original Init ____________________)
The catch here is that after doing all the first stage mounting, /init
will pivot /system as root directory (/), leaving us impossible to
regain control after we hand it over. So the solution here is to patch
fstab in /first_stage_ramdisk on-the-fly to redirect /system to
/system_root, making the original init do all the hard work for
us and mount required early mount partitions, but skips the step of
switching root directory. It will also conveniently hand over execution
back to MagiskInit, which we will reuse the routine for patching
root directory in normal system-as-root situations.
2019-06-29 09:47:29 +02:00
|
|
|
file_readline("/init.rc", [=](string_view line) -> bool {
|
2019-06-26 08:31:59 +02:00
|
|
|
// Do not start vaultkeeper
|
|
|
|
if (str_contains(line, "start vaultkeeper")) {
|
|
|
|
LOGD("Remove vaultkeeper\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Do not run flash_recovery
|
|
|
|
if (str_starts(line, "service flash_recovery")) {
|
|
|
|
LOGD("Remove flash_recovery\n");
|
|
|
|
fprintf(rc, "service flash_recovery /system/bin/xxxxx\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Else just write the line
|
|
|
|
fprintf(rc, "%s", line.data());
|
|
|
|
return true;
|
|
|
|
});
|
2019-07-16 10:08:28 +02:00
|
|
|
|
|
|
|
fprintf(rc, "\n");
|
|
|
|
|
|
|
|
// Inject custom rc scripts
|
|
|
|
for (auto &d : rc_list)
|
|
|
|
fprintf(rc, "\n%s\n", d.buf);
|
|
|
|
rc_list.clear();
|
|
|
|
|
|
|
|
// Inject Magisk rc scripts
|
2019-07-15 06:56:21 +02:00
|
|
|
char pfd_svc[16], ls_svc[16], bc_svc[16];
|
2019-07-15 02:41:51 +02:00
|
|
|
gen_rand_str(pfd_svc, sizeof(pfd_svc));
|
|
|
|
gen_rand_str(ls_svc, sizeof(ls_svc));
|
|
|
|
gen_rand_str(bc_svc, sizeof(bc_svc));
|
2019-06-26 08:31:59 +02:00
|
|
|
LOGD("Inject magisk services: [%s] [%s] [%s]\n", pfd_svc, ls_svc, bc_svc);
|
|
|
|
fprintf(rc, magiskrc, pfd_svc, pfd_svc, ls_svc, bc_svc, bc_svc);
|
|
|
|
}
|
2019-05-27 09:29:43 +02:00
|
|
|
|
2019-07-16 10:08:28 +02:00
|
|
|
static void load_overlay_rc(int dirfd) {
|
|
|
|
// Do not allow overwrite init.rc
|
|
|
|
unlinkat(dirfd, "init.rc", 0);
|
|
|
|
DIR *dir = fdopendir(dirfd);
|
|
|
|
for (dirent *entry; (entry = readdir(dir));) {
|
|
|
|
if (strend(entry->d_name, ".rc") == 0) {
|
|
|
|
LOGD("Found rc script [%s]\n", entry->d_name);
|
|
|
|
int rc = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
|
|
|
raw_data data;
|
|
|
|
fd_full_read(rc, data.buf, data.sz);
|
|
|
|
close(rc);
|
|
|
|
rc_list.push_back(std::move(data));
|
|
|
|
unlinkat(dirfd, entry->d_name, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rewinddir(dir);
|
|
|
|
}
|
|
|
|
|
2019-12-12 09:25:48 +01:00
|
|
|
void RootFSInit::setup_rootfs() {
|
2019-06-24 10:31:42 +02:00
|
|
|
if (patch_sepolicy()) {
|
2019-05-27 09:29:43 +02:00
|
|
|
char *addr;
|
|
|
|
size_t size;
|
|
|
|
mmap_rw("/init", addr, size);
|
|
|
|
for (char *p = addr; p < addr + size; ++p) {
|
|
|
|
if (memcmp(p, SPLIT_PLAT_CIL, sizeof(SPLIT_PLAT_CIL)) == 0) {
|
|
|
|
// Force init to load /sepolicy
|
|
|
|
LOGD("Remove from init: " SPLIT_PLAT_CIL "\n");
|
|
|
|
memset(p, 'x', sizeof(SPLIT_PLAT_CIL) - 1);
|
2019-06-25 11:38:34 +02:00
|
|
|
break;
|
2019-05-27 09:29:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
munmap(addr, size);
|
|
|
|
}
|
|
|
|
|
2019-07-16 10:08:28 +02:00
|
|
|
// Handle overlays
|
2019-12-13 14:31:24 +01:00
|
|
|
int fd = open("/overlay.d", O_RDONLY | O_CLOEXEC);
|
2019-07-16 10:08:28 +02:00
|
|
|
if (fd >= 0) {
|
|
|
|
LOGD("Merge overlay.d\n");
|
|
|
|
load_overlay_rc(fd);
|
|
|
|
mv_dir(fd, root);
|
|
|
|
close(fd);
|
|
|
|
rmdir("/overlay.d");
|
|
|
|
}
|
|
|
|
|
2019-05-27 09:29:43 +02:00
|
|
|
// Patch init.rc
|
|
|
|
FILE *rc = xfopen("/init.p.rc", "we");
|
2019-06-26 08:31:59 +02:00
|
|
|
patch_init_rc(rc);
|
2019-05-27 09:29:43 +02:00
|
|
|
fclose(rc);
|
|
|
|
clone_attr("/init.rc", "/init.p.rc");
|
|
|
|
rename("/init.p.rc", "/init.rc");
|
|
|
|
|
|
|
|
// Create hardlink mirror of /sbin to /root
|
|
|
|
mkdir("/root", 0750);
|
|
|
|
clone_attr("/sbin", "/root");
|
|
|
|
int rootdir = xopen("/root", O_RDONLY | O_CLOEXEC);
|
|
|
|
int sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
|
|
|
|
link_dir(sbin, rootdir);
|
|
|
|
close(sbin);
|
|
|
|
|
2019-06-22 12:14:33 +02:00
|
|
|
// Dump magiskinit as magisk
|
|
|
|
fd = xopen("/sbin/magisk", O_WRONLY | O_CREAT, 0755);
|
2019-05-27 09:29:43 +02:00
|
|
|
write(fd, self.buf, self.sz);
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
2019-06-26 06:34:02 +02:00
|
|
|
bool MagiskInit::patch_sepolicy(const char *file) {
|
2019-10-20 13:13:03 +02:00
|
|
|
bool patch_init = false;
|
2019-05-27 09:29:43 +02:00
|
|
|
|
|
|
|
if (access(SPLIT_PLAT_CIL, R_OK) == 0) {
|
|
|
|
LOGD("sepol: split policy\n");
|
2019-10-20 13:13:03 +02:00
|
|
|
patch_init = true;
|
2019-05-27 09:29:43 +02:00
|
|
|
} else if (access("/sepolicy", R_OK) == 0) {
|
|
|
|
LOGD("sepol: monolithic policy\n");
|
|
|
|
load_policydb("/sepolicy");
|
|
|
|
} else {
|
|
|
|
LOGD("sepol: no selinux\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-10-20 13:13:03 +02:00
|
|
|
// Mount selinuxfs to communicate with kernel
|
|
|
|
xmount("selinuxfs", SELINUX_MNT, "selinuxfs", 0, nullptr);
|
2019-11-19 06:16:20 +01:00
|
|
|
mount_list.emplace_back(SELINUX_MNT);
|
2019-10-20 13:13:03 +02:00
|
|
|
|
|
|
|
if (patch_init)
|
|
|
|
load_split_cil();
|
|
|
|
|
2019-05-27 09:29:43 +02:00
|
|
|
sepol_magisk_rules();
|
|
|
|
sepol_allow(SEPOL_PROC_DOMAIN, ALL, ALL, ALL);
|
2019-12-13 12:05:12 +01:00
|
|
|
|
|
|
|
// Custom rules
|
|
|
|
if (auto dir = xopen_dir(persist_dir); dir) {
|
|
|
|
char path[4096];
|
|
|
|
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
|
|
|
if (entry->d_name == "."sv || entry->d_name == ".."sv)
|
|
|
|
continue;
|
|
|
|
snprintf(path, sizeof(path), "%s/%s/sepolicy.rule", persist_dir, entry->d_name);
|
|
|
|
if (access(path, R_OK) == 0) {
|
|
|
|
LOGD("Loading custom sepolicy patch: %s\n", path);
|
|
|
|
load_rule_file(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-26 06:34:02 +02:00
|
|
|
dump_policydb(file);
|
2019-12-13 12:05:12 +01:00
|
|
|
destroy_policydb();
|
2019-05-27 09:29:43 +02:00
|
|
|
|
|
|
|
// Remove OnePlus stupid debug sepolicy and use our own
|
|
|
|
if (access("/sepolicy_debug", F_OK) == 0) {
|
|
|
|
unlink("/sepolicy_debug");
|
|
|
|
link("/sepolicy", "/sepolicy_debug");
|
|
|
|
}
|
|
|
|
|
2019-10-20 13:13:03 +02:00
|
|
|
return patch_init;
|
2019-05-27 09:29:43 +02:00
|
|
|
}
|
2019-06-22 12:14:33 +02:00
|
|
|
|
2019-06-24 10:21:33 +02:00
|
|
|
static void sbin_overlay(const raw_data &self, const raw_data &config) {
|
2019-12-09 08:44:49 +01:00
|
|
|
mount_sbin();
|
2019-06-24 10:21:33 +02:00
|
|
|
|
|
|
|
// Dump binaries
|
|
|
|
xmkdir(MAGISKTMP, 0755);
|
|
|
|
int fd = xopen(MAGISKTMP "/config", O_WRONLY | O_CREAT, 0000);
|
|
|
|
xwrite(fd, config.buf, config.sz);
|
|
|
|
close(fd);
|
|
|
|
fd = xopen("/sbin/magiskinit", O_WRONLY | O_CREAT, 0755);
|
|
|
|
xwrite(fd, self.buf, self.sz);
|
|
|
|
close(fd);
|
2020-01-10 20:20:59 +01:00
|
|
|
dump_magisk("/sbin/magisk", 0755);
|
|
|
|
patch_socket_name("/sbin/magisk");
|
2019-06-24 10:21:33 +02:00
|
|
|
|
|
|
|
// Create applet symlinks
|
|
|
|
char path[64];
|
|
|
|
for (int i = 0; applet_names[i]; ++i) {
|
|
|
|
sprintf(path, "/sbin/%s", applet_names[i]);
|
2019-07-05 02:58:38 +02:00
|
|
|
xsymlink("./magisk", path);
|
2019-06-24 10:21:33 +02:00
|
|
|
}
|
2019-07-05 02:58:38 +02:00
|
|
|
xsymlink("./magiskinit", "/sbin/magiskpolicy");
|
|
|
|
xsymlink("./magiskinit", "/sbin/supolicy");
|
2019-06-24 10:21:33 +02:00
|
|
|
}
|
|
|
|
|
2020-01-09 16:42:27 +01:00
|
|
|
static void recreate_sbin(const char *mirror, bool use_bind_mount) {
|
|
|
|
auto dp = xopen_dir(mirror);
|
|
|
|
int src = dirfd(dp.get());
|
|
|
|
char buf[4096];
|
|
|
|
for (dirent *entry; (entry = xreaddir(dp.get()));) {
|
2019-12-06 21:31:49 +01:00
|
|
|
if (entry->d_name == "."sv || entry->d_name == ".."sv)
|
|
|
|
continue;
|
2020-01-09 16:42:27 +01:00
|
|
|
string sbin_path = "/sbin/"s + entry->d_name;
|
2019-12-06 21:31:49 +01:00
|
|
|
struct stat st;
|
|
|
|
fstatat(src, entry->d_name, &st, AT_SYMLINK_NOFOLLOW);
|
|
|
|
if (S_ISLNK(st.st_mode)) {
|
|
|
|
xreadlinkat(src, entry->d_name, buf, sizeof(buf));
|
2020-01-09 16:42:27 +01:00
|
|
|
xsymlink(buf, sbin_path.data());
|
2019-12-06 21:31:49 +01:00
|
|
|
} else {
|
|
|
|
sprintf(buf, "%s/%s", mirror, entry->d_name);
|
|
|
|
if (use_bind_mount) {
|
2020-01-09 16:42:27 +01:00
|
|
|
auto mode = st.st_mode & 0777;
|
2019-12-06 21:31:49 +01:00
|
|
|
// Create dummy
|
|
|
|
if (S_ISDIR(st.st_mode))
|
2020-01-09 16:42:27 +01:00
|
|
|
xmkdir(sbin_path.data(), mode);
|
2019-12-06 21:31:49 +01:00
|
|
|
else
|
2020-01-09 16:42:27 +01:00
|
|
|
close(xopen(sbin_path.data(), O_CREAT | O_WRONLY | O_CLOEXEC, mode));
|
2019-12-06 21:31:49 +01:00
|
|
|
|
2020-01-09 16:42:27 +01:00
|
|
|
xmount(buf, sbin_path.data(), nullptr, MS_BIND, nullptr);
|
|
|
|
} else {
|
|
|
|
xsymlink(buf, sbin_path.data());
|
|
|
|
}
|
2019-12-06 21:31:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-24 10:21:33 +02:00
|
|
|
#define ROOTMIR MIRRDIR "/system_root"
|
|
|
|
#define ROOTBLK BLOCKDIR "/system_root"
|
2019-06-26 06:34:02 +02:00
|
|
|
#define MONOPOLICY "/sepolicy"
|
|
|
|
#define PATCHPOLICY "/sbin/.se"
|
|
|
|
#define LIBSELINUX "/system/" LIBNAME "/libselinux.so"
|
2019-06-24 10:21:33 +02:00
|
|
|
|
2019-11-19 06:16:20 +01:00
|
|
|
static string magic_mount_list;
|
2019-07-17 08:54:52 +02:00
|
|
|
|
2020-02-21 09:49:58 +01:00
|
|
|
static void magic_mount(const string &sdir, const string &ddir = "") {
|
|
|
|
auto dir = xopen_dir(sdir.data());
|
|
|
|
for (dirent *entry; (entry = readdir(dir.get()));) {
|
2019-07-16 10:08:28 +02:00
|
|
|
if (entry->d_name == "."sv || entry->d_name == ".."sv)
|
|
|
|
continue;
|
2020-02-21 09:49:58 +01:00
|
|
|
string src = sdir + "/" + entry->d_name;
|
|
|
|
string dest = ddir + "/" + entry->d_name;
|
2019-07-16 10:08:28 +02:00
|
|
|
if (access(dest.data(), F_OK) == 0) {
|
|
|
|
if (entry->d_type == DT_DIR) {
|
|
|
|
// Recursive
|
2020-02-21 09:49:58 +01:00
|
|
|
magic_mount(src, dest);
|
2019-07-16 10:08:28 +02:00
|
|
|
} else {
|
|
|
|
LOGD("Mount [%s] -> [%s]\n", src.data(), dest.data());
|
|
|
|
xmount(src.data(), dest.data(), nullptr, MS_BIND, nullptr);
|
2019-11-19 06:16:20 +01:00
|
|
|
magic_mount_list += dest;
|
|
|
|
magic_mount_list += '\n';
|
2019-07-16 10:08:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-22 11:20:51 +02:00
|
|
|
void SARBase::patch_rootdir() {
|
2019-06-24 10:21:33 +02:00
|
|
|
sbin_overlay(self, config);
|
|
|
|
|
|
|
|
// Mount system_root mirror
|
2019-12-09 08:44:49 +01:00
|
|
|
xmkdir(ROOTMIR, 0755);
|
2019-06-24 10:21:33 +02:00
|
|
|
mknod(ROOTBLK, S_IFBLK | 0600, system_dev);
|
|
|
|
if (xmount(ROOTBLK, ROOTMIR, "ext4", MS_RDONLY, nullptr))
|
|
|
|
xmount(ROOTBLK, ROOTMIR, "erofs", MS_RDONLY, nullptr);
|
|
|
|
|
|
|
|
// Recreate original sbin structure
|
2020-01-09 16:42:27 +01:00
|
|
|
recreate_sbin(ROOTMIR "/sbin", true);
|
2019-06-26 06:34:02 +02:00
|
|
|
|
2019-07-16 10:08:28 +02:00
|
|
|
// Patch init
|
|
|
|
raw_data init;
|
2019-06-26 06:34:02 +02:00
|
|
|
file_attr attr;
|
|
|
|
bool redirect = false;
|
2019-12-06 21:31:49 +01:00
|
|
|
int src = xopen("/init", O_RDONLY | O_CLOEXEC);
|
2019-07-16 10:08:28 +02:00
|
|
|
fd_full_read(src, init.buf, init.sz);
|
2019-06-26 06:34:02 +02:00
|
|
|
fgetattr(src, &attr);
|
|
|
|
close(src);
|
2019-07-16 10:08:28 +02:00
|
|
|
uint8_t *eof = init.buf + init.sz;
|
|
|
|
for (uint8_t *p = init.buf; p < eof; ++p) {
|
2019-06-26 06:34:02 +02:00
|
|
|
if (memcmp(p, SPLIT_PLAT_CIL, sizeof(SPLIT_PLAT_CIL)) == 0) {
|
|
|
|
// Force init to load monolithic policy
|
|
|
|
LOGD("Remove from init: " SPLIT_PLAT_CIL "\n");
|
|
|
|
memset(p, 'x', sizeof(SPLIT_PLAT_CIL) - 1);
|
|
|
|
p += sizeof(SPLIT_PLAT_CIL) - 1;
|
|
|
|
} else if (memcmp(p, MONOPOLICY, sizeof(MONOPOLICY)) == 0) {
|
|
|
|
// Redirect /sepolicy to tmpfs
|
|
|
|
LOGD("Patch init [" MONOPOLICY "] -> [" PATCHPOLICY "]\n");
|
|
|
|
memcpy(p, PATCHPOLICY, sizeof(PATCHPOLICY));
|
|
|
|
redirect = true;
|
|
|
|
p += sizeof(MONOPOLICY) - 1;
|
|
|
|
}
|
|
|
|
}
|
2019-07-16 10:08:28 +02:00
|
|
|
xmkdir(ROOTOVL, 0);
|
2019-12-06 21:31:49 +01:00
|
|
|
int dest = xopen(ROOTOVL "/init", O_CREAT | O_WRONLY | O_CLOEXEC);
|
2019-07-16 10:08:28 +02:00
|
|
|
xwrite(dest, init.buf, init.sz);
|
2019-06-26 06:34:02 +02:00
|
|
|
fsetattr(dest, &attr);
|
|
|
|
close(dest);
|
|
|
|
|
2019-07-16 10:08:28 +02:00
|
|
|
// Patch libselinux
|
2019-06-26 06:34:02 +02:00
|
|
|
if (!redirect) {
|
2019-07-16 10:08:28 +02:00
|
|
|
raw_data lib;
|
2019-06-26 06:34:02 +02:00
|
|
|
// init is dynamically linked, need to patch libselinux
|
2019-07-16 10:08:28 +02:00
|
|
|
full_read(LIBSELINUX, lib.buf, lib.sz);
|
2019-06-26 06:34:02 +02:00
|
|
|
getattr(LIBSELINUX, &attr);
|
2019-07-16 10:08:28 +02:00
|
|
|
eof = lib.buf + lib.sz;
|
|
|
|
for (uint8_t *p = lib.buf; p < eof; ++p) {
|
2019-06-26 06:34:02 +02:00
|
|
|
if (memcmp(p, MONOPOLICY, sizeof(MONOPOLICY)) == 0) {
|
|
|
|
// Redirect /sepolicy to tmpfs
|
|
|
|
LOGD("Patch libselinux.so [" MONOPOLICY "] -> [" PATCHPOLICY "]\n");
|
|
|
|
memcpy(p, PATCHPOLICY, sizeof(PATCHPOLICY));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-07-16 10:08:28 +02:00
|
|
|
xmkdir(ROOTOVL "/system", 0755);
|
|
|
|
xmkdir(ROOTOVL "/system/" LIBNAME, 0755);
|
|
|
|
dest = xopen(ROOTOVL LIBSELINUX, O_CREAT | O_WRONLY | O_CLOEXEC);
|
|
|
|
xwrite(dest, lib.buf, lib.sz);
|
2019-06-26 06:34:02 +02:00
|
|
|
fsetattr(dest, &attr);
|
|
|
|
close(dest);
|
|
|
|
}
|
|
|
|
|
2019-07-16 10:08:28 +02:00
|
|
|
// sepolicy
|
2019-06-26 06:34:02 +02:00
|
|
|
patch_sepolicy(PATCHPOLICY);
|
2019-06-26 08:31:59 +02:00
|
|
|
|
2019-07-16 10:08:28 +02:00
|
|
|
// Handle overlay
|
|
|
|
if ((src = xopen("/dev/overlay.d", O_RDONLY | O_CLOEXEC)) >= 0) {
|
|
|
|
load_overlay_rc(src);
|
|
|
|
if (int fd = xopen("/dev/overlay.d/sbin", O_RDONLY | O_CLOEXEC); fd >= 0) {
|
|
|
|
dest = xopen("/sbin", O_RDONLY | O_CLOEXEC);
|
|
|
|
clone_dir(fd, dest);
|
|
|
|
close(fd);
|
|
|
|
close(dest);
|
|
|
|
xmkdir(ROOTOVL "/sbin", 0); // Prevent copying
|
|
|
|
}
|
|
|
|
dest = xopen(ROOTOVL, O_RDONLY | O_CLOEXEC);
|
|
|
|
clone_dir(src, dest, false);
|
|
|
|
rmdir(ROOTOVL "/sbin");
|
|
|
|
close(src);
|
|
|
|
close(dest);
|
|
|
|
rm_rf("/dev/overlay.d");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Patch init.rc
|
|
|
|
FILE *rc = xfopen(ROOTOVL "/init.rc", "we");
|
2019-06-26 08:31:59 +02:00
|
|
|
patch_init_rc(rc);
|
|
|
|
fclose(rc);
|
2019-07-16 10:08:28 +02:00
|
|
|
clone_attr("/init.rc", ROOTOVL "/init.rc");
|
|
|
|
|
|
|
|
// Mount rootdir
|
2020-02-21 09:49:58 +01:00
|
|
|
magic_mount(ROOTOVL);
|
2019-07-17 08:54:52 +02:00
|
|
|
dest = xopen(ROOTMNT, O_WRONLY | O_CREAT | O_CLOEXEC);
|
2019-11-19 06:16:20 +01:00
|
|
|
write(dest, magic_mount_list.data(), magic_mount_list.length());
|
2019-07-17 08:54:52 +02:00
|
|
|
close(dest);
|
2019-06-24 10:21:33 +02:00
|
|
|
}
|
|
|
|
|
2019-09-22 11:30:04 +02:00
|
|
|
static void patch_fstab(const string &fstab) {
|
Logical Resizable Android Partitions support
The way how logical partition, or "Logical Resizable Android Partitions"
as they say in AOSP source code, is setup makes it impossible to early
mount the partitions from the shared super partition with just
a few lines of code; in fact, AOSP has a whole "fs_mgr" folder which
consist of multiple complex libraries, with 15K lines of code just
to deal with the device mapper shenanigans.
In order to keep the already overly complicated MagiskInit more
managable, I chose NOT to go the route of including fs_mgr directly
into MagiskInit. Luckily, starting from Android Q, Google decided to
split init startup into 3 stages, with the first stage doing _only_
early mount. This is great news, because we can simply let the stock
init do its own thing for us, and we intercept the bootup sequence.
So the workflow can be visualized roughly below:
Magisk First Stage --> First Stage Mount --> Magisk Second Stage --+
(MagiskInit) (Original Init) (MagiskInit) +
+
+
...Rest of the boot... <-- Second Stage <-- Selinux Setup <--+
(__________________ Original Init ____________________)
The catch here is that after doing all the first stage mounting, /init
will pivot /system as root directory (/), leaving us impossible to
regain control after we hand it over. So the solution here is to patch
fstab in /first_stage_ramdisk on-the-fly to redirect /system to
/system_root, making the original init do all the hard work for
us and mount required early mount partitions, but skips the step of
switching root directory. It will also conveniently hand over execution
back to MagiskInit, which we will reuse the routine for patching
root directory in normal system-as-root situations.
2019-06-29 09:47:29 +02:00
|
|
|
string patched = fstab + ".p";
|
|
|
|
FILE *fp = xfopen(patched.data(), "we");
|
|
|
|
file_readline(fstab.data(), [=](string_view l) -> bool {
|
|
|
|
if (l[0] == '#' || l.length() == 1)
|
|
|
|
return true;
|
|
|
|
char *line = (char *) l.data();
|
|
|
|
int src0, src1, mnt0, mnt1, type0, type1, opt0, opt1, flag0, flag1;
|
|
|
|
sscanf(line, "%n%*s%n %n%*s%n %n%*s%n %n%*s%n %n%*s%n",
|
|
|
|
&src0, &src1, &mnt0, &mnt1, &type0, &type1, &opt0, &opt1, &flag0, &flag1);
|
|
|
|
const char *src, *mnt, *type, *opt, *flag;
|
|
|
|
src = &line[src0];
|
|
|
|
line[src1] = '\0';
|
|
|
|
mnt = &line[mnt0];
|
|
|
|
line[mnt1] = '\0';
|
|
|
|
type = &line[type0];
|
|
|
|
line[type1] = '\0';
|
|
|
|
opt = &line[opt0];
|
|
|
|
line[opt1] = '\0';
|
|
|
|
flag = &line[flag0];
|
|
|
|
line[flag1] = '\0';
|
|
|
|
|
|
|
|
// Redirect system to system_root
|
|
|
|
if (mnt == "/system"sv)
|
|
|
|
mnt = "/system_root";
|
|
|
|
|
|
|
|
fprintf(fp, "%s %s %s %s %s\n", src, mnt, type, opt, flag);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
// Replace old fstab
|
|
|
|
clone_attr(fstab.data(), patched.data());
|
|
|
|
rename(patched.data(), fstab.data());
|
2019-09-22 11:30:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#define FSR "/first_stage_ramdisk"
|
|
|
|
|
|
|
|
void ABFirstStageInit::prepare() {
|
2020-01-21 22:12:04 +01:00
|
|
|
// It is actually possible to NOT have FSR, create it just in case
|
|
|
|
xmkdir(FSR, 0755);
|
|
|
|
|
|
|
|
if (auto dir = xopen_dir(FSR); dir) {
|
|
|
|
string fstab(FSR "/");
|
|
|
|
for (dirent *de; (de = xreaddir(dir.get()));) {
|
|
|
|
if (strstr(de->d_name, "fstab")) {
|
|
|
|
fstab += de->d_name;
|
|
|
|
break;
|
|
|
|
}
|
2019-09-22 11:30:04 +02:00
|
|
|
}
|
2020-01-21 22:12:04 +01:00
|
|
|
if (fstab.length() == sizeof(FSR))
|
|
|
|
return;
|
2019-09-22 11:30:04 +02:00
|
|
|
|
2020-01-21 22:12:04 +01:00
|
|
|
patch_fstab(fstab);
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
Logical Resizable Android Partitions support
The way how logical partition, or "Logical Resizable Android Partitions"
as they say in AOSP source code, is setup makes it impossible to early
mount the partitions from the shared super partition with just
a few lines of code; in fact, AOSP has a whole "fs_mgr" folder which
consist of multiple complex libraries, with 15K lines of code just
to deal with the device mapper shenanigans.
In order to keep the already overly complicated MagiskInit more
managable, I chose NOT to go the route of including fs_mgr directly
into MagiskInit. Luckily, starting from Android Q, Google decided to
split init startup into 3 stages, with the first stage doing _only_
early mount. This is great news, because we can simply let the stock
init do its own thing for us, and we intercept the bootup sequence.
So the workflow can be visualized roughly below:
Magisk First Stage --> First Stage Mount --> Magisk Second Stage --+
(MagiskInit) (Original Init) (MagiskInit) +
+
+
...Rest of the boot... <-- Second Stage <-- Selinux Setup <--+
(__________________ Original Init ____________________)
The catch here is that after doing all the first stage mounting, /init
will pivot /system as root directory (/), leaving us impossible to
regain control after we hand it over. So the solution here is to patch
fstab in /first_stage_ramdisk on-the-fly to redirect /system to
/system_root, making the original init do all the hard work for
us and mount required early mount partitions, but skips the step of
switching root directory. It will also conveniently hand over execution
back to MagiskInit, which we will reuse the routine for patching
root directory in normal system-as-root situations.
2019-06-29 09:47:29 +02:00
|
|
|
|
|
|
|
// Move stuffs for next stage
|
|
|
|
xmkdir(FSR "/system", 0755);
|
|
|
|
xmkdir(FSR "/system/bin", 0755);
|
|
|
|
rename("/init", FSR "/system/bin/init");
|
2019-07-16 10:08:28 +02:00
|
|
|
symlink("/system/bin/init", FSR "/init");
|
Logical Resizable Android Partitions support
The way how logical partition, or "Logical Resizable Android Partitions"
as they say in AOSP source code, is setup makes it impossible to early
mount the partitions from the shared super partition with just
a few lines of code; in fact, AOSP has a whole "fs_mgr" folder which
consist of multiple complex libraries, with 15K lines of code just
to deal with the device mapper shenanigans.
In order to keep the already overly complicated MagiskInit more
managable, I chose NOT to go the route of including fs_mgr directly
into MagiskInit. Luckily, starting from Android Q, Google decided to
split init startup into 3 stages, with the first stage doing _only_
early mount. This is great news, because we can simply let the stock
init do its own thing for us, and we intercept the bootup sequence.
So the workflow can be visualized roughly below:
Magisk First Stage --> First Stage Mount --> Magisk Second Stage --+
(MagiskInit) (Original Init) (MagiskInit) +
+
+
...Rest of the boot... <-- Second Stage <-- Selinux Setup <--+
(__________________ Original Init ____________________)
The catch here is that after doing all the first stage mounting, /init
will pivot /system as root directory (/), leaving us impossible to
regain control after we hand it over. So the solution here is to patch
fstab in /first_stage_ramdisk on-the-fly to redirect /system to
/system_root, making the original init do all the hard work for
us and mount required early mount partitions, but skips the step of
switching root directory. It will also conveniently hand over execution
back to MagiskInit, which we will reuse the routine for patching
root directory in normal system-as-root situations.
2019-06-29 09:47:29 +02:00
|
|
|
xmkdir(FSR "/.backup", 0);
|
|
|
|
rename("/.backup/.magisk", FSR "/.backup/.magisk");
|
2019-07-16 10:08:28 +02:00
|
|
|
rename("/overlay.d", FSR "/overlay.d");
|
Logical Resizable Android Partitions support
The way how logical partition, or "Logical Resizable Android Partitions"
as they say in AOSP source code, is setup makes it impossible to early
mount the partitions from the shared super partition with just
a few lines of code; in fact, AOSP has a whole "fs_mgr" folder which
consist of multiple complex libraries, with 15K lines of code just
to deal with the device mapper shenanigans.
In order to keep the already overly complicated MagiskInit more
managable, I chose NOT to go the route of including fs_mgr directly
into MagiskInit. Luckily, starting from Android Q, Google decided to
split init startup into 3 stages, with the first stage doing _only_
early mount. This is great news, because we can simply let the stock
init do its own thing for us, and we intercept the bootup sequence.
So the workflow can be visualized roughly below:
Magisk First Stage --> First Stage Mount --> Magisk Second Stage --+
(MagiskInit) (Original Init) (MagiskInit) +
+
+
...Rest of the boot... <-- Second Stage <-- Selinux Setup <--+
(__________________ Original Init ____________________)
The catch here is that after doing all the first stage mounting, /init
will pivot /system as root directory (/), leaving us impossible to
regain control after we hand it over. So the solution here is to patch
fstab in /first_stage_ramdisk on-the-fly to redirect /system to
/system_root, making the original init do all the hard work for
us and mount required early mount partitions, but skips the step of
switching root directory. It will also conveniently hand over execution
back to MagiskInit, which we will reuse the routine for patching
root directory in normal system-as-root situations.
2019-06-29 09:47:29 +02:00
|
|
|
}
|
|
|
|
|
2019-09-22 11:15:31 +02:00
|
|
|
void AFirstStageInit::prepare() {
|
2019-12-13 06:37:06 +01:00
|
|
|
auto dir = xopen_dir("/");
|
|
|
|
for (dirent *de; (de = xreaddir(dir.get()));) {
|
2019-09-22 11:30:04 +02:00
|
|
|
if (strstr(de->d_name, "fstab")) {
|
|
|
|
patch_fstab(de->d_name);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-22 11:15:31 +02:00
|
|
|
// Move stuffs for next stage
|
|
|
|
xmkdir("/system", 0755);
|
|
|
|
xmkdir("/system/bin", 0755);
|
|
|
|
rename("/init", "/system/bin/init");
|
|
|
|
rename("/.backup/init", "/init");
|
|
|
|
}
|
|
|
|
|
2019-06-22 12:14:33 +02:00
|
|
|
int magisk_proxy_main(int argc, char *argv[]) {
|
|
|
|
setup_klog();
|
|
|
|
|
|
|
|
raw_data config;
|
|
|
|
raw_data self;
|
|
|
|
|
2019-07-16 10:08:28 +02:00
|
|
|
full_read("/sbin/magisk", self.buf, self.sz);
|
|
|
|
full_read("/.backup/.magisk", config.buf, config.sz);
|
2019-06-22 12:14:33 +02:00
|
|
|
|
|
|
|
xmount(nullptr, "/", nullptr, MS_REMOUNT, nullptr);
|
|
|
|
|
|
|
|
unlink("/sbin/magisk");
|
|
|
|
rm_rf("/.backup");
|
|
|
|
|
2019-06-24 10:21:33 +02:00
|
|
|
sbin_overlay(self, config);
|
2019-06-22 12:14:33 +02:00
|
|
|
|
|
|
|
// Create symlinks pointing back to /root
|
2020-01-09 16:42:27 +01:00
|
|
|
recreate_sbin("/root", false);
|
2019-06-22 12:14:33 +02:00
|
|
|
|
|
|
|
setenv("REMOUNT_ROOT", "1", 1);
|
|
|
|
execv("/sbin/magisk", argv);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|