From 7ba8202af5f99cdcc8ba0eac1ad91f1b11fa57a8 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 16 Jul 2019 01:08:28 -0700 Subject: [PATCH] Introduce new root overlay system --- native/jni/include/magisk.h | 1 - native/jni/init/early_mount.cpp | 46 +++++------ native/jni/init/init.h | 25 ++++-- native/jni/init/magiskrc.h | 4 +- native/jni/init/rootdir.cpp | 140 ++++++++++++++++++++++++++------ native/jni/utils/files.h | 12 +++ 6 files changed, 169 insertions(+), 59 deletions(-) diff --git a/native/jni/include/magisk.h b/native/jni/include/magisk.h index 89dca4512..4ea5557ce 100644 --- a/native/jni/include/magisk.h +++ b/native/jni/include/magisk.h @@ -12,7 +12,6 @@ #define BLOCKDIR MAGISKTMP "/block" #define BBPATH MAGISKTMP "/busybox" #define MODULEMNT MAGISKTMP "/modules" -#define ROOTOVERLAY MAGISKTMP "/rootdir" #define SECURE_DIR "/data/adb" #define MODULEROOT SECURE_DIR "/modules" #define MODULEUPGRADE SECURE_DIR "/modules_update" diff --git a/native/jni/init/early_mount.cpp b/native/jni/init/early_mount.cpp index 7e29f5105..bc8dcda27 100644 --- a/native/jni/init/early_mount.cpp +++ b/native/jni/init/early_mount.cpp @@ -104,6 +104,10 @@ bool MagiskInit::read_dt_fstab(const char *name, char *partname, char *fstype) { return false; } +static char partname[32]; +static char fstype[32]; +static char block_dev[64]; + #define link_root(name) \ if (is_lnk("/system_root" name)) \ cp_afc("/system_root" name, name) @@ -118,11 +122,7 @@ if (!is_lnk("/" #name) && read_dt_fstab(#name, partname, fstype)) { \ } void LegacyInit::early_mount() { - char partname[32]; - char fstype[32]; - char block_dev[64]; - - full_read("/init", &self.buf, &self.sz); + full_read("/init", self.buf, self.sz); LOGD("Reverting /init\n"); root = xopen("/", O_RDONLY | O_CLOEXEC); @@ -135,15 +135,11 @@ void LegacyInit::early_mount() { } void SARCompatInit::early_mount() { - char partname[32]; - char fstype[32]; - char block_dev[64]; - - full_read("/init", &self.buf, &self.sz); + full_read("/init", self.buf, self.sz); LOGD("Cleaning rootfs\n"); root = xopen("/", O_RDONLY | O_CLOEXEC); - frm_rf(root, { ".backup", "overlay", "proc", "sys" }); + frm_rf(root, { ".backup", "overlay", "overlay.d", "proc", "sys" }); LOGD("Early mount system_root\n"); sprintf(partname, "system%s", cmd->slot); @@ -187,17 +183,24 @@ static void switch_root(const string &path) { chroot("."); } -void SARInit::early_mount() { - char partname[32]; - char fstype[32]; - char block_dev[64]; +void SARCommon::backup_files() { + if (access("/overlay.d", F_OK) == 0) + cp_afc("/overlay.d", "/dev/overlay.d"); - full_read("/init", &self.buf, &self.sz); - full_read("/.backup/.magisk", &config.buf, &config.sz); + full_read("/init", self.buf, self.sz); + full_read("/.backup/.magisk", config.buf, config.sz); +} + +void SARInit::early_mount() { + // Make dev writable + xmkdir("/dev", 0755); + xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755"); + + backup_files(); LOGD("Cleaning rootfs\n"); int root = xopen("/", O_RDONLY | O_CLOEXEC); - frm_rf(root, { "proc", "sys" }); + frm_rf(root, { "proc", "sys", "dev" }); close(root); LOGD("Early mount system_root\n"); @@ -208,9 +211,6 @@ void SARInit::early_mount() { xmount("/dev/root", "/system_root", "erofs", MS_RDONLY, nullptr); switch_root("/system_root"); - // Make dev writable - xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755"); - mount_root(vendor); mount_root(product); mount_root(odm); @@ -219,10 +219,10 @@ void SARInit::early_mount() { void SecondStageInit::early_mount() { // Early mounts should already be done by first stage init - full_read("/system/bin/init", &self.buf, &self.sz); - full_read("/.backup/.magisk", &config.buf, &config.sz); + backup_files(); rm_rf("/system"); rm_rf("/.backup"); + rm_rf("/overlay.d"); // Find system_dev parse_mnt("/proc/mounts", [&](mntent *me) -> bool { diff --git a/native/jni/init/init.h b/native/jni/init/init.h index c60e4f744..53c6e530c 100644 --- a/native/jni/init/init.h +++ b/native/jni/init/init.h @@ -10,8 +10,20 @@ struct cmdline { }; struct raw_data { - void *buf; - size_t sz; + uint8_t *buf = nullptr; + size_t sz = 0; + + raw_data() = default; + raw_data(const raw_data&) = delete; + raw_data(raw_data &&d) { + d.buf = buf; + d.sz = sz; + buf = nullptr; + sz = 0; + } + ~raw_data() { + free(buf); + } }; /* ************* @@ -41,7 +53,7 @@ public: class MagiskInit : public BaseInit { protected: - raw_data self{}; + raw_data self; bool mnt_system = false; bool mnt_vendor = false; bool mnt_product = false; @@ -71,9 +83,10 @@ public: class SARCommon : public MagiskInit { protected: - raw_data config{}; + raw_data config; dev_t system_dev; + void backup_files(); void patch_rootdir(); public: SARCommon(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {}; @@ -90,11 +103,11 @@ public: class FirstStageInit : public BaseInit { protected: - void patch_fstab(); + void prepare(); public: FirstStageInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {}; void start() override { - patch_fstab(); + prepare(); exec_init("/system/bin/init"); } }; diff --git a/native/jni/init/magiskrc.h b/native/jni/init/magiskrc.h index 1e9a1b7a1..d9e9c264e 100644 --- a/native/jni/init/magiskrc.h +++ b/native/jni/init/magiskrc.h @@ -1,8 +1,8 @@ #include #include -constexpr const char magiskrc[] = -"\n\n" +constexpr char magiskrc[] = +"\n" "on post-fs-data\n" " start logd\n" diff --git a/native/jni/init/rootdir.cpp b/native/jni/init/rootdir.cpp index 9e9cd0ada..288f396cc 100644 --- a/native/jni/init/rootdir.cpp +++ b/native/jni/init/rootdir.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -31,6 +32,8 @@ static void patch_socket_name(const char *path) { munmap(buf, size); } +static vector rc_list; + static void patch_init_rc(FILE *rc) { file_readline("/init.rc", [=](string_view line) -> bool { // Do not start vaultkeeper @@ -48,6 +51,15 @@ static void patch_init_rc(FILE *rc) { fprintf(rc, "%s", line.data()); return true; }); + + 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 char pfd_svc[16], ls_svc[16], bc_svc[16]; gen_rand_str(pfd_svc, sizeof(pfd_svc)); gen_rand_str(ls_svc, sizeof(ls_svc)); @@ -56,6 +68,24 @@ static void patch_init_rc(FILE *rc) { fprintf(rc, magiskrc, pfd_svc, pfd_svc, ls_svc, bc_svc, bc_svc); } +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); +} + void RootFSInit::setup_rootfs() { if (patch_sepolicy()) { char *addr; @@ -72,7 +102,7 @@ void RootFSInit::setup_rootfs() { munmap(addr, size); } - // Handle ramdisk overlays + // Handle legacy overlays int fd = open("/overlay", O_RDONLY | O_CLOEXEC); if (fd >= 0) { LOGD("Merge overlay folder\n"); @@ -81,6 +111,16 @@ void RootFSInit::setup_rootfs() { rmdir("/overlay"); } + // Handle overlays + fd = open("/overlay.d", O_RDONLY | O_CLOEXEC); + if (fd >= 0) { + LOGD("Merge overlay.d\n"); + load_overlay_rc(fd); + mv_dir(fd, root); + close(fd); + rmdir("/overlay.d"); + } + // Patch init.rc FILE *rc = xfopen("/init.p.rc", "we"); patch_init_rc(rc); @@ -185,18 +225,40 @@ static void sbin_overlay(const raw_data &self, const raw_data &config) { xsymlink("./magiskinit", "/sbin/supolicy"); } +#define ROOTOVL MAGISKTMP "/rootdir" #define ROOTMIR MIRRDIR "/system_root" #define ROOTBLK BLOCKDIR "/system_root" #define MONOPOLICY "/sepolicy" #define PATCHPOLICY "/sbin/.se" #define LIBSELINUX "/system/" LIBNAME "/libselinux.so" +static void magic_mount(int dirfd, const string &path) { + DIR *dir = xfdopendir(dirfd); + for (dirent *entry; (entry = readdir(dir));) { + if (entry->d_name == "."sv || entry->d_name == ".."sv) + continue; + string dest = path + "/" + entry->d_name; + if (access(dest.data(), F_OK) == 0) { + if (entry->d_type == DT_DIR) { + // Recursive + int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC); + magic_mount(fd, dest); + close(fd); + } else { + string src = ROOTOVL + dest; + LOGD("Mount [%s] -> [%s]\n", src.data(), dest.data()); + xmount(src.data(), dest.data(), nullptr, MS_BIND, nullptr); + } + } + } +} + void SARCommon::patch_rootdir() { sbin_overlay(self, config); // Mount system_root mirror xmkdir(MIRRDIR, 0); - xmkdir(ROOTMIR, 0755); + xmkdir(ROOTMIR, 0); xmkdir(BLOCKDIR, 0); mknod(ROOTBLK, S_IFBLK | 0600, system_dev); if (xmount(ROOTBLK, ROOTMIR, "ext4", MS_RDONLY, nullptr)) @@ -231,20 +293,16 @@ void SARCommon::patch_rootdir() { close(src); close(dest); - /* ****************** - * Customize rootdir - * ******************/ - - char *addr; - size_t size; + // Patch init + raw_data init; file_attr attr; bool redirect = false; - xmkdir(ROOTOVERLAY, 0); src = xopen("/init", O_RDONLY | O_CLOEXEC); + fd_full_read(src, init.buf, init.sz); fgetattr(src, &attr); - fd_full_read(src, (void**)&addr, &size); close(src); - for (char *p = addr; p < addr + size; ++p) { + uint8_t *eof = init.buf + init.sz; + for (uint8_t *p = init.buf; p < eof; ++p) { 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"); @@ -258,18 +316,20 @@ void SARCommon::patch_rootdir() { p += sizeof(MONOPOLICY) - 1; } } - dest = xopen(ROOTOVERLAY "/init", O_CREAT | O_WRONLY | O_CLOEXEC); - xwrite(dest, addr, size); - free(addr); + xmkdir(ROOTOVL, 0); + dest = xopen(ROOTOVL "/init", O_CREAT | O_WRONLY | O_CLOEXEC); + xwrite(dest, init.buf, init.sz); fsetattr(dest, &attr); close(dest); - xmount(ROOTOVERLAY "/init", "/init", nullptr, MS_BIND, nullptr); + // Patch libselinux if (!redirect) { + raw_data lib; // init is dynamically linked, need to patch libselinux - full_read(LIBSELINUX, (void**)&addr, &size); + full_read(LIBSELINUX, lib.buf, lib.sz); getattr(LIBSELINUX, &attr); - for (char *p = addr; p < addr + size; ++p) { + eof = lib.buf + lib.sz; + for (uint8_t *p = lib.buf; p < eof; ++p) { if (memcmp(p, MONOPOLICY, sizeof(MONOPOLICY)) == 0) { // Redirect /sepolicy to tmpfs LOGD("Patch libselinux.so [" MONOPOLICY "] -> [" PATCHPOLICY "]\n"); @@ -277,26 +337,50 @@ void SARCommon::patch_rootdir() { break; } } - dest = xopen(ROOTOVERLAY "/libselinux.so", O_CREAT | O_WRONLY | O_CLOEXEC); - xwrite(dest, addr, size); - free(addr); + 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); fsetattr(dest, &attr); close(dest); - xmount(ROOTOVERLAY "/libselinux.so", LIBSELINUX, nullptr, MS_BIND, nullptr); } + // sepolicy patch_sepolicy(PATCHPOLICY); - FILE *rc = xfopen(ROOTOVERLAY "/init.rc", "we"); + // 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"); patch_init_rc(rc); fclose(rc); - clone_attr("/init.rc", ROOTOVERLAY "/init.rc"); - xmount(ROOTOVERLAY "/init.rc", "/init.rc", nullptr, MS_BIND, nullptr); + clone_attr("/init.rc", ROOTOVL "/init.rc"); + + // Mount rootdir + src = xopen(ROOTOVL, O_RDONLY | O_CLOEXEC); + magic_mount(src, ""); + close(src); } #define FSR "/first_stage_ramdisk" -void FirstStageInit::patch_fstab() { +void FirstStageInit::prepare() { // Find fstab DIR *dir = xopendir(FSR); if (!dir) @@ -352,8 +436,10 @@ void FirstStageInit::patch_fstab() { xmkdir(FSR "/system", 0755); xmkdir(FSR "/system/bin", 0755); rename("/init", FSR "/system/bin/init"); + symlink("/system/bin/init", FSR "/init"); xmkdir(FSR "/.backup", 0); rename("/.backup/.magisk", FSR "/.backup/.magisk"); + rename("/overlay.d", FSR "/overlay.d"); } #ifdef MAGISK_DEBUG @@ -379,8 +465,8 @@ int magisk_proxy_main(int argc, char *argv[]) { raw_data config; raw_data self; - full_read("/sbin/magisk", &self.buf, &self.sz); - full_read("/.backup/.magisk", &config.buf, &config.sz); + full_read("/sbin/magisk", self.buf, self.sz); + full_read("/.backup/.magisk", config.buf, config.sz); xmount(nullptr, "/", nullptr, MS_REMOUNT, nullptr); diff --git a/native/jni/utils/files.h b/native/jni/utils/files.h index b9ed9f9e2..e38a5e8af 100644 --- a/native/jni/utils/files.h +++ b/native/jni/utils/files.h @@ -46,6 +46,18 @@ void frm_rf(int dirfd, std::initializer_list excl = std::initializ void clone_dir(int src, int dest, bool overwrite = true); void parse_mnt(const char *file, const std::function &fn); +template +void full_read(const char *filename, T &buf, size_t &size) { + static_assert(std::is_pointer::value); + full_read(filename, reinterpret_cast(&buf), &size); +} + +template +void fd_full_read(int fd, T &buf, size_t &size) { + static_assert(std::is_pointer::value); + fd_full_read(fd, reinterpret_cast(&buf), &size); +} + template void mmap_ro(const char *filename, B &buf, size_t &sz) { buf = (B) __mmap(filename, &sz, false);