diff --git a/native/jni/core/init.cpp b/native/jni/core/init.cpp index 300cb72a1..518d4ee6e 100644 --- a/native/jni/core/init.cpp +++ b/native/jni/core/init.cpp @@ -53,19 +53,10 @@ #define DEFAULT_DT_DIR "/proc/device-tree/firmware/android" -int (*init_applet_main[]) (int, char *[]) = { magiskpolicy_main, magiskpolicy_main, nullptr }; - -static bool mnt_system = false; -static bool mnt_vendor = false; -static bool mnt_product = false; -static bool mnt_odm = false; -static bool kirin = false; - -static void *self, *config; -static size_t self_sz, config_sz; +int (*init_applet_main[])(int, char *[]) = { magiskpolicy_main, magiskpolicy_main, nullptr }; struct cmdline { - bool early_boot; + bool system_as_root; char slot[3]; char dt_dir[128]; }; @@ -78,192 +69,10 @@ struct device { char path[64]; }; -static void parse_cmdline(const std::function &fn) { - char cmdline[4096]; - int fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC); - cmdline[read(fd, cmdline, sizeof(cmdline))] = '\0'; - close(fd); - - char *tok, *eql, *tmp, *saveptr; - saveptr = cmdline; - while ((tok = strtok_r(nullptr, " \n", &saveptr)) != nullptr) { - eql = strchr(tok, '='); - if (eql) { - *eql = '\0'; - if (eql[1] == '"') { - tmp = strchr(saveptr, '"'); - if (tmp != nullptr) { - *tmp = '\0'; - saveptr[-1] = ' '; - saveptr = tmp + 1; - eql++; - } - } - fn(tok, eql + 1); - } else { - fn(tok, ""); - } - } -} - -static void parse_cmdline(struct cmdline *cmd) { - char cmdline[4096]; - int fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC); - cmdline[read(fd, cmdline, sizeof(cmdline))] = '\0'; - close(fd); - - bool skip_initramfs = false, enter_recovery = false; - - parse_cmdline([&](auto key, auto value) -> void { - LOGD("cmdline: [%s]=[%s]\n", key.data(), value); - if (key == "androidboot.slot_suffix") { - strcpy(cmd->slot, value); - } else if (key == "androidboot.slot") { - cmd->slot[0] = '_'; - strcpy(cmd->slot + 1, value); - } else if (key == "skip_initramfs") { - skip_initramfs = true; - } else if (key == "androidboot.android_dt_dir") { - strcpy(cmd->dt_dir, value); - } else if (key == "enter_recovery") { - enter_recovery = value[0] == '1'; - } else if (key == "androidboot.hardware") { - kirin = strstr(value, "kirin") || strstr(value, "hi3660"); - } - }); - - if (kirin && enter_recovery) { - // Inform that we are actually booting as recovery - if (FILE *f = fopen("/.backup/.magisk", "ae"); f) { - fprintf(f, "RECOVERYMODE=true\n"); - fclose(f); - } - cmd->early_boot = true; - } - - - cmd->early_boot |= skip_initramfs; - - if (cmd->dt_dir[0] == '\0') - strcpy(cmd->dt_dir, DEFAULT_DT_DIR); - - LOGD("cmdline: early_boot[%d] slot[%s] dt_dir[%s]\n", cmd->early_boot, cmd->slot, cmd->dt_dir); -} - -static void parse_device(struct device *dev, const char *uevent) { - dev->partname[0] = '\0'; - FILE *fp = xfopen(uevent, "r"); - char buf[64]; - while (fgets(buf, sizeof(buf), fp)) { - if (strncmp(buf, "MAJOR", 5) == 0) { - sscanf(buf, "MAJOR=%ld", (long*) &dev->major); - } else if (strncmp(buf, "MINOR", 5) == 0) { - sscanf(buf, "MINOR=%ld", (long*) &dev->minor); - } else if (strncmp(buf, "DEVNAME", 7) == 0) { - sscanf(buf, "DEVNAME=%s", dev->devname); - } else if (strncmp(buf, "PARTNAME", 8) == 0) { - sscanf(buf, "PARTNAME=%s", dev->partname); - } - } - fclose(fp); - LOGD("%s [%s] (%u, %u)\n", dev->devname, dev->partname, (unsigned) dev->major, (unsigned) dev->minor); -} - -static bool setup_block(struct device *dev, const char *partname) { - char path[128]; - struct dirent *entry; - DIR *dir = opendir("/sys/dev/block"); - if (dir == nullptr) - return false; - bool found = false; - while ((entry = readdir(dir))) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - sprintf(path, "/sys/dev/block/%s/uevent", entry->d_name); - parse_device(dev, path); - if (strcasecmp(dev->partname, partname) == 0) { - sprintf(dev->path, "/dev/block/%s", dev->devname); - found = true; - break; - } - } - closedir(dir); - - if (!found) - return false; - - mkdir("/dev", 0755); - mkdir("/dev/block", 0755); - mknod(dev->path, S_IFBLK | 0600, makedev(dev->major, dev->minor)); - return true; -} - -static inline bool is_lnk(const char *name) { - struct stat st; - if (lstat(name, &st)) - return false; - return S_ISLNK(st.st_mode); -} - -static bool read_fstab_dt(const struct cmdline *cmd, const char *mnt_point, char *partname, char *partfs) { - char buf[128]; - struct stat st; - sprintf(buf, "/%s", mnt_point); - if (is_lnk(buf)) - return false; - int fd; - sprintf(buf, "%s/fstab/%s/dev", cmd->dt_dir, mnt_point); - if ((fd = xopen(buf, O_RDONLY | O_CLOEXEC)) >= 0) { - read(fd, buf, sizeof(buf)); - close(fd); - char *name = rtrim(strrchr(buf, '/') + 1); - sprintf(partname, "%s%s", name, strend(name, cmd->slot) ? cmd->slot : ""); - sprintf(buf, "%s/fstab/%s/type", cmd->dt_dir, mnt_point); - if ((fd = xopen(buf, O_RDONLY | O_CLOEXEC)) >= 0) { - lstat(buf, &st); - read(fd, partfs, st.st_size); - close(fd); - return true; - } - } - return false; -} - -static bool patch_sepolicy() { - bool patch_init = false; - - if (access(SPLIT_PLAT_CIL, R_OK) == 0) - patch_init = true; /* Split sepolicy */ - else if (access("/sepolicy", R_OK) == 0) - load_policydb("/sepolicy"); /* Monolithic sepolicy */ - else - return false; /* No SELinux */ - - // Mount selinuxfs to communicate with kernel - xmount("selinuxfs", SELINUX_MNT, "selinuxfs", 0, nullptr); - - if (patch_init) - load_split_cil(); - - sepol_magisk_rules(); - sepol_allow(SEPOL_PROC_DOMAIN, ALL, ALL, ALL); - dump_policydb("/sepolicy"); - - // Load policy to kernel so we can label rootfs - if (!kirin) - dump_policydb(SELINUX_LOAD); - - // Remove OnePlus stupid debug sepolicy and use our own - if (access("/sepolicy_debug", F_OK) == 0) { - unlink("/sepolicy_debug"); - link("/sepolicy", "/sepolicy_debug"); - } - - // Enable selinux functions - selinux_builtin_impl(); - - return patch_init; -} +struct raw_data { + void *buf; + size_t sz; +}; static bool unxz(int fd, const uint8_t *buf, size_t size) { uint8_t out[8192]; @@ -326,157 +135,116 @@ static int dump_manager(const char *path, mode_t mode) { return 0; } -static void patch_socket_name(const char *path) { - uint8_t *buf; - char name[sizeof(MAIN_SOCKET)]; - 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) { - gen_rand_str(name, sizeof(name)); - memcpy(buf + i, name, sizeof(name)); - i += sizeof(name); - } - if (memcmp(buf + i, LOG_SOCKET, sizeof(LOG_SOCKET)) == 0) { - gen_rand_str(name, sizeof(name)); - memcpy(buf + i, name, sizeof(name)); - i += sizeof(name); +class MagiskInit { +private: + cmdline cmd{}; + raw_data init{}; + raw_data config{}; + int root = -1; + bool mnt_system = false; + bool mnt_vendor = false; + bool mnt_product = false; + bool mnt_odm = false; + bool kirin = false; + char **argv; + + void load_kernel_info(); + void preset(); + void early_mount(); + void setup_rootfs(); + bool read_dt_fstab(const char *mnt_point, char *partname, char *partfs); + bool patch_sepolicy(); + void cleanup(); + +public: + explicit MagiskInit(char *argv[]) : argv(argv) {} + void setup_overlay(); + void re_exec_init(); + void start(); + void test(); +}; + +static inline void parse_cmdline(const std::function &fn) { + char cmdline[4096]; + int fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC); + cmdline[read(fd, cmdline, sizeof(cmdline))] = '\0'; + close(fd); + + char *tok, *eql, *tmp, *saveptr; + saveptr = cmdline; + while ((tok = strtok_r(nullptr, " \n", &saveptr)) != nullptr) { + eql = strchr(tok, '='); + if (eql) { + *eql = '\0'; + if (eql[1] == '"') { + tmp = strchr(saveptr, '"'); + if (tmp != nullptr) { + *tmp = '\0'; + saveptr[-1] = ' '; + saveptr = tmp + 1; + eql++; + } + } + fn(tok, eql + 1); + } else { + fn(tok, ""); } } - munmap(buf, size); } -static void setup_init_rc() { - FILE *rc = xfopen("/init.rc", "ae"); - char pfd_svc[8], ls_svc[8]; - gen_rand_str(pfd_svc, sizeof(pfd_svc)); - do { - gen_rand_str(ls_svc, sizeof(ls_svc)); - } while (strcmp(pfd_svc, ls_svc) == 0); - fprintf(rc, magiskrc, pfd_svc, pfd_svc, ls_svc); - fclose(rc); -} - -static void setup_overlay() { - char buf[128]; - int fd; - - // Wait for early-init start - while (access(EARLYINIT, F_OK) != 0) - usleep(10); - setcon("u:r:" SEPOL_PROC_DOMAIN ":s0"); - unlink(EARLYINIT); - - // Mount the /sbin tmpfs overlay - xmount("tmpfs", "/sbin", "tmpfs", 0, nullptr); - chmod("/sbin", 0755); - setfilecon("/sbin", "u:object_r:rootfs:s0"); - - // Dump binaries - mkdir(MAGISKTMP, 0755); - fd = xopen(MAGISKTMP "/config", O_WRONLY | O_CREAT, 0000); - write(fd, config, config_sz); - close(fd); - fd = xopen("/sbin/magiskinit", O_WRONLY | O_CREAT, 0755); - write(fd, self, self_sz); - close(fd); - dump_magisk("/sbin/magisk", 0755); - patch_socket_name("/sbin/magisk"); - setfilecon("/sbin/magisk", "u:object_r:" SEPOL_FILE_DOMAIN ":s0"); - setfilecon("/sbin/magiskinit", "u:object_r:" SEPOL_FILE_DOMAIN ":s0"); - - // Create applet symlinks - for (int i = 0; applet_names[i]; ++i) { - sprintf(buf, "/sbin/%s", applet_names[i]); - xsymlink("/sbin/magisk", buf); - } - for (int i = 0; init_applet[i]; ++i) { - sprintf(buf, "/sbin/%s", init_applet[i]); - xsymlink("/sbin/magiskinit", buf); - } - - // Create symlinks pointing back to /root - DIR *dir; - struct dirent *entry; - dir = xopendir("/root"); - fd = xopen("/sbin", O_RDONLY); - while((entry = xreaddir(dir))) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; - snprintf(buf, PATH_MAX, "/root/%s", entry->d_name); - xsymlinkat(buf, fd, entry->d_name); - } - closedir(dir); - close(fd); - - close(xopen(EARLYINITDONE, O_RDONLY | O_CREAT, 0)); - exit(0); -} - -[[noreturn]] static void exec_init(char *argv[]) { - // Clean up - umount(SELINUX_MNT); - umount("/sys"); - umount("/proc"); - if (mnt_system) - umount("/system"); - if (mnt_vendor) - umount("/vendor"); - if (mnt_product) - umount("/product"); - if (mnt_odm) - umount("/odm"); - - execv("/init", argv); - exit(1); -} - -int main(int argc, char *argv[]) { - umask(0); - no_logging(); - - for (int i = 0; init_applet[i]; ++i) { - if (strcmp(basename(argv[0]), init_applet[i]) == 0) - return (*init_applet_main[i])(argc, argv); - } - - if (argc > 1 && strcmp(argv[1], "-x") == 0) { - if (strcmp(argv[2], "magisk") == 0) - return dump_magisk(argv[3], 0755); - else if (strcmp(argv[2], "manager") == 0) - return dump_manager(argv[3], 0644); - } - - // Prevent file descriptor confusion - mknod("/null", S_IFCHR | 0666, makedev(1, 3)); - int null = open("/null", O_RDWR | O_CLOEXEC); - unlink("/null"); - xdup3(null, STDIN_FILENO, O_CLOEXEC); - xdup3(null, STDOUT_FILENO, O_CLOEXEC); - xdup3(null, STDERR_FILENO, O_CLOEXEC); - if (null > STDERR_FILENO) - close(null); - +void MagiskInit::load_kernel_info() { // Communicate with kernel using procfs and sysfs mkdir("/proc", 0755); xmount("proc", "/proc", "proc", 0, nullptr); mkdir("/sys", 0755); xmount("sysfs", "/sys", "sysfs", 0, nullptr); - struct cmdline cmd{}; - parse_cmdline(&cmd); + char cmdline[4096]; + int fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC); + cmdline[read(fd, cmdline, sizeof(cmdline))] = '\0'; + close(fd); - // Backup stuffs - full_read("/init", &self, &self_sz); - full_read("/.backup/.magisk", &config, &config_sz); + bool skip_initramfs = false, enter_recovery = false; - /************* - * Initialize - *************/ + parse_cmdline([&](auto key, auto value) -> void { + LOGD("cmdline: [%s]=[%s]\n", key.data(), value); + if (key == "androidboot.slot_suffix") { + strcpy(cmd.slot, value); + } else if (key == "androidboot.slot") { + cmd.slot[0] = '_'; + strcpy(cmd.slot + 1, value); + } else if (key == "skip_initramfs") { + skip_initramfs = true; + } else if (key == "androidboot.android_dt_dir") { + strcpy(cmd.dt_dir, value); + } else if (key == "enter_recovery") { + enter_recovery = value[0] == '1'; + } else if (key == "androidboot.hardware") { + kirin = strstr(value, "kirin") || strstr(value, "hi3660"); + } + }); - int root, sbin; + if (kirin && enter_recovery) { + // Inform that we are actually booting as recovery + if (FILE *f = fopen("/.backup/.magisk", "ae"); f) { + fprintf(f, "RECOVERYMODE=true\n"); + fclose(f); + } + cmd.system_as_root = true; + } + + cmd.system_as_root |= skip_initramfs; + + if (cmd.dt_dir[0] == '\0') + strcpy(cmd.dt_dir, DEFAULT_DT_DIR); + + LOGD("system_as_root[%d] slot[%s] dt_dir[%s]\n", cmd.system_as_root, cmd.slot, cmd.dt_dir); +} + +void MagiskInit::preset() { root = open("/", O_RDONLY | O_CLOEXEC); - if (cmd.early_boot) { + if (cmd.system_as_root) { // Clear rootfs const char *excl[] = { "overlay", "proc", "sys", nullptr }; excl_list = excl; @@ -491,18 +259,83 @@ int main(int argc, char *argv[]) { // Do not go further if device is booting into recovery if (access("/sbin/recovery", F_OK) == 0) - exec_init(argv); + re_exec_init(); } +} - /************** - * Early Mount - **************/ +static inline void parse_device(struct device *dev, const char *uevent) { + dev->partname[0] = '\0'; + FILE *fp = xfopen(uevent, "r"); + char buf[64]; + while (fgets(buf, sizeof(buf), fp)) { + if (strncmp(buf, "MAJOR", 5) == 0) { + sscanf(buf, "MAJOR=%ld", (long*) &dev->major); + } else if (strncmp(buf, "MINOR", 5) == 0) { + sscanf(buf, "MINOR=%ld", (long*) &dev->minor); + } else if (strncmp(buf, "DEVNAME", 7) == 0) { + sscanf(buf, "DEVNAME=%s", dev->devname); + } else if (strncmp(buf, "PARTNAME", 8) == 0) { + sscanf(buf, "PARTNAME=%s", dev->partname); + } + } + fclose(fp); + LOGD("%s [%s] (%u, %u)\n", dev->devname, dev->partname, (unsigned) dev->major, (unsigned) dev->minor); +} +static bool setup_block(struct device *dev, const char *partname) { + char path[128]; + struct dirent *entry; + DIR *dir = opendir("/sys/dev/block"); + if (dir == nullptr) + return false; + bool found = false; + while ((entry = readdir(dir))) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + sprintf(path, "/sys/dev/block/%s/uevent", entry->d_name); + parse_device(dev, path); + if (strcasecmp(dev->partname, partname) == 0) { + sprintf(dev->path, "/dev/block/%s", dev->devname); + found = true; + break; + } + } + closedir(dir); + + if (!found) + return false; + + mkdir("/dev", 0755); + mkdir("/dev/block", 0755); + mknod(dev->path, S_IFBLK | 0600, makedev(dev->major, dev->minor)); + return true; +} + +static inline bool is_lnk(const char *name) { + struct stat st; + if (lstat(name, &st)) + return false; + return S_ISLNK(st.st_mode); +} + +#define link_root(part) \ +if (is_lnk("/system_root" part)) \ + cp_afc("/system_root" part, part) + +#define mount_root(part) \ +if (!is_lnk("/" #part) && read_dt_fstab(#part, partname, fstype)) { \ + setup_block(&dev, partname); \ + xmkdir("/" #part, 0755); \ + xmount(dev.path, "/" #part, fstype, MS_RDONLY, nullptr); \ + mnt_##part = true; \ +} + +void MagiskInit::early_mount() { struct device dev; char partname[32]; - char partfs[32]; + char fstype[32]; - if (cmd.early_boot) { + if (cmd.system_as_root) { sprintf(partname, "system%s", cmd.slot); setup_block(&dev, partname); xmkdir("/system_root", 0755); @@ -511,47 +344,22 @@ int main(int argc, char *argv[]) { xmount("/system_root/system", "/system", nullptr, MS_BIND, nullptr); // Copy if these partitions are symlinks - if (is_lnk("/system_root/vendor")) - cp_afc("/system_root/vendor", "/vendor"); - if (is_lnk("/system_root/product")) - cp_afc("/system_root/product", "/product"); - if (is_lnk("/system_root/odm")) - cp_afc("/system_root/odm", "/odm"); - - } else if (read_fstab_dt(&cmd, "system", partname, partfs)) { - setup_block(&dev, partname); - xmount(dev.path, "/system", partfs, MS_RDONLY, nullptr); - mnt_system = true; + link_root("/vendor"); + link_root("/product"); + link_root("/odm"); + } else { + mount_root(system); } - if (read_fstab_dt(&cmd, "vendor", partname, partfs)) { - setup_block(&dev, partname); - xmkdir("/vendor", 0755); - xmount(dev.path, "/vendor", partfs, MS_RDONLY, nullptr); - mnt_vendor = true; - } - - if (read_fstab_dt(&cmd, "product", partname, partfs)) { - setup_block(&dev, partname); - xmkdir("/product", 0755); - xmount(dev.path, "/product", partfs, MS_RDONLY, nullptr); - mnt_product = true; - } - - if (read_fstab_dt(&cmd, "odm", partname, partfs)) { - setup_block(&dev, partname); - xmkdir("/odm", 0755); - xmount(dev.path, "/odm", partfs, MS_RDONLY, nullptr); - mnt_odm = true; - } - - /*************** - * Setup Rootfs - ***************/ + mount_root(vendor); + mount_root(product); + mount_root(odm); +} +void MagiskInit::setup_rootfs() { bool patch_init = patch_sepolicy(); - if (cmd.early_boot) { + if (cmd.system_as_root) { // Clone rootfs except /system int system_root = open("/system_root", O_RDONLY | O_CLOEXEC); const char *excl[] = { "system", nullptr }; @@ -594,7 +402,15 @@ int main(int argc, char *argv[]) { rmdir("/overlay"); } - setup_init_rc(); + // Patch init.rc + FILE *rc = xfopen("/init.rc", "ae"); + char pfd_svc[8], ls_svc[8]; + gen_rand_str(pfd_svc, sizeof(pfd_svc)); + do { + gen_rand_str(ls_svc, sizeof(ls_svc)); + } while (strcmp(pfd_svc, ls_svc) == 0); + fprintf(rc, magiskrc, pfd_svc, pfd_svc, ls_svc); + fclose(rc); // Don't let init run in init yet lsetfilecon("/init", "u:object_r:rootfs:s0"); @@ -602,9 +418,215 @@ int main(int argc, char *argv[]) { // Create hardlink mirror of /sbin to /root mkdir("/root", 0750); clone_attr("/sbin", "/root"); - root = xopen("/root", O_RDONLY | O_CLOEXEC); - sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC); - link_dir(sbin, root); + int rootdir = xopen("/root", O_RDONLY | O_CLOEXEC); + int sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC); + link_dir(sbin, rootdir); +} + +bool MagiskInit::patch_sepolicy() { + bool patch_init = false; + + if (access(SPLIT_PLAT_CIL, R_OK) == 0) + patch_init = true; /* Split sepolicy */ + else if (access("/sepolicy", R_OK) == 0) + load_policydb("/sepolicy"); /* Monolithic sepolicy */ + else + return false; /* No SELinux */ + + // Mount selinuxfs to communicate with kernel + xmount("selinuxfs", SELINUX_MNT, "selinuxfs", 0, nullptr); + + if (patch_init) + load_split_cil(); + + sepol_magisk_rules(); + sepol_allow(SEPOL_PROC_DOMAIN, ALL, ALL, ALL); + dump_policydb("/sepolicy"); + + // Load policy to kernel so we can label rootfs + if (!kirin) + dump_policydb(SELINUX_LOAD); + + // Remove OnePlus stupid debug sepolicy and use our own + if (access("/sepolicy_debug", F_OK) == 0) { + unlink("/sepolicy_debug"); + link("/sepolicy", "/sepolicy_debug"); + } + + // Enable selinux functions + selinux_builtin_impl(); + + return patch_init; +} + +bool MagiskInit::read_dt_fstab(const char *mnt_point, char *partname, char *partfs) { + char path[128]; + int fd; + sprintf(path, "%s/fstab/%s/dev", cmd.dt_dir, mnt_point); + if ((fd = xopen(path, O_RDONLY | O_CLOEXEC)) >= 0) { + read(fd, path, sizeof(path)); + close(fd); + char *name = rtrim(strrchr(path, '/') + 1); + sprintf(partname, "%s%s", name, strend(name, cmd.slot) ? cmd.slot : ""); + sprintf(path, "%s/fstab/%s/type", cmd.dt_dir, mnt_point); + if ((fd = xopen(path, O_RDONLY | O_CLOEXEC)) >= 0) { + read(fd, partfs, 32); + close(fd); + return true; + } + } + return false; +} + +#define umount_part(part) \ +if (mnt_##part) \ + umount("/" #part); + +void MagiskInit::cleanup() { + umount(SELINUX_MNT); + umount("/sys"); + umount("/proc"); + umount_part(system); + umount_part(vendor); + umount_part(product); + umount_part(odm); +} + +static inline void patch_socket_name(const char *path) { + uint8_t *buf; + char name[sizeof(MAIN_SOCKET)]; + 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) { + gen_rand_str(name, sizeof(name)); + memcpy(buf + i, name, sizeof(name)); + i += sizeof(name); + } + if (memcmp(buf + i, LOG_SOCKET, sizeof(LOG_SOCKET)) == 0) { + gen_rand_str(name, sizeof(name)); + memcpy(buf + i, name, sizeof(name)); + i += sizeof(name); + } + } + munmap(buf, size); +} + +void MagiskInit::setup_overlay() { + char path[128]; + int fd; + + // Wait for early-init start + while (access(EARLYINIT, F_OK) != 0) + usleep(10); + setcon("u:r:" SEPOL_PROC_DOMAIN ":s0"); + unlink(EARLYINIT); + + // Mount the /sbin tmpfs overlay + xmount("tmpfs", "/sbin", "tmpfs", 0, nullptr); + chmod("/sbin", 0755); + setfilecon("/sbin", "u:object_r:rootfs:s0"); + + // Dump binaries + mkdir(MAGISKTMP, 0755); + fd = xopen(MAGISKTMP "/config", O_WRONLY | O_CREAT, 0000); + write(fd, config.buf, config.sz); + close(fd); + fd = xopen("/sbin/magiskinit", O_WRONLY | O_CREAT, 0755); + write(fd, init.buf, init.sz); + close(fd); + dump_magisk("/sbin/magisk", 0755); + patch_socket_name("/sbin/magisk"); + setfilecon("/sbin/magisk", "u:object_r:" SEPOL_FILE_DOMAIN ":s0"); + setfilecon("/sbin/magiskinit", "u:object_r:" SEPOL_FILE_DOMAIN ":s0"); + + // Create applet symlinks + for (int i = 0; applet_names[i]; ++i) { + sprintf(path, "/sbin/%s", applet_names[i]); + xsymlink("/sbin/magisk", path); + } + for (int i = 0; init_applet[i]; ++i) { + sprintf(path, "/sbin/%s", init_applet[i]); + xsymlink("/sbin/magiskinit", path); + } + + // Create symlinks pointing back to /root + DIR *dir = xopendir("/root"); + struct dirent *entry; + fd = xopen("/sbin", O_RDONLY); + while((entry = xreaddir(dir))) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + snprintf(path, PATH_MAX, "/root/%s", entry->d_name); + xsymlinkat(path, fd, entry->d_name); + } + closedir(dir); + close(fd); + + close(xopen(EARLYINITDONE, O_RDONLY | O_CREAT, 0)); + exit(0); +} + +void MagiskInit::re_exec_init() { + cleanup(); + execv("/init", argv); + exit(1); +} + +void MagiskInit::start() { + // Prevent file descriptor confusion + mknod("/null", S_IFCHR | 0666, makedev(1, 3)); + int null = open("/null", O_RDWR | O_CLOEXEC); + unlink("/null"); + xdup3(null, STDIN_FILENO, O_CLOEXEC); + xdup3(null, STDOUT_FILENO, O_CLOEXEC); + xdup3(null, STDERR_FILENO, O_CLOEXEC); + if (null > STDERR_FILENO) + close(null); + + full_read("/init", &init.buf, &init.sz); + full_read("/.backup/.magisk", &config.buf, &config.sz); + + load_kernel_info(); + preset(); + early_mount(); + setup_rootfs(); +} + +void MagiskInit::test() { + cmdline_logging(); + log_cb.ex = nop_ex; + + chdir(argv[1]); + chroot("."); + chdir("/"); + + load_kernel_info(); + preset(); + early_mount(); + setup_rootfs(); + cleanup(); +} + +int main(int argc, char *argv[]) { + umask(0); + + for (int i = 0; init_applet[i]; ++i) { + if (strcmp(basename(argv[0]), init_applet[i]) == 0) + return (*init_applet_main[i])(argc, argv); + } + + if (argc > 1 && strcmp(argv[1], "-x") == 0) { + if (strcmp(argv[2], "magisk") == 0) + return dump_magisk(argv[3], 0755); + else if (strcmp(argv[2], "manager") == 0) + return dump_manager(argv[3], 0644); + } + + MagiskInit init(argv); + + // Run the main routine + init.start(); // Close all file descriptors for (int i = 0; i < 30; ++i) @@ -612,7 +634,7 @@ int main(int argc, char *argv[]) { // Launch daemon to setup overlay if (fork_dont_care() == 0) - setup_overlay(); + init.setup_overlay(); - exec_init(argv); + init.re_exec_init(); }