From a31c1e80849f38f1d6f016bb754447ee02c7bf5c Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 4 May 2017 01:13:04 +0800 Subject: [PATCH] post-fs-data mode done --- jni/daemon/bootstages.c | 246 +++++++++++++++++++++++++++++++++------ jni/daemon/log_monitor.c | 6 +- jni/magisk.h | 12 ++ jni/utils/misc.c | 114 +++++++++++++++++- jni/utils/utils.h | 7 +- jni/utils/xwrap.c | 24 ++++ 6 files changed, 361 insertions(+), 48 deletions(-) diff --git a/jni/daemon/bootstages.c b/jni/daemon/bootstages.c index 78a63bda0..056dd61da 100644 --- a/jni/daemon/bootstages.c +++ b/jni/daemon/bootstages.c @@ -74,6 +74,111 @@ static char *mount_image(const char *img, const char *target) { return device; } +static void umount_image(const char *target, const char *device) { + xumount(target); + int fd = xopen(device, O_RDWR); + ioctl(fd, LOOP_CLR_FD); + close(fd); +} + +static int get_img_size(const char *img, int *used, int *total) { + char buffer[PATH_MAX]; + snprintf(buffer, sizeof(buffer), "e2fsck -n %s 2>/dev/null | tail -n 1", img); + char *const command[] = { "sh", "-c", buffer, NULL }; + int pid, fd; + pid = run_command(&fd, "/system/bin/sh", command); + fdgets(buffer, sizeof(buffer), fd); + close(fd); + if (pid == -1) + return 1; + waitpid(pid, NULL, 0); + char *tok; + tok = strtok(buffer, ","); + while(tok != NULL) { + if (strstr(tok, "blocks")) + break; + tok = strtok(NULL, ","); + } + sscanf(tok, "%d/%d", used, total); + *used = *used / 256 + 1; + *total /= 256; + return 0; +} + +#define round_size(a) ((((a) / 32) + 1) * 32) + +static int resize_img(const char *img, int size) { + char buffer[ARG_MAX]; + LOGI("resize %s to %dM\n", img, size); + snprintf(buffer, sizeof(buffer), "e2fsck -yf %s && resize2fs %s %dM;", img, img, size); + return system(buffer); +} + +static int merge_img(const char *source, const char *target) { + if (access(source, F_OK) == -1) + return 0; + if (access(target, F_OK) == -1) { + rename(source, target); + return 0; + } + + // resize target to worst case + int s_used, s_total, t_used, t_total, n_total; + get_img_size(source, &s_used, &s_total); + get_img_size(target, &t_used, &t_total); + n_total = round_size(s_used + t_used); + if (n_total != t_total && resize_img(target, n_total)) + return 1; + + xmkdir("/cache/source", 0755); + xmkdir("/cache/target", 0755); + char *s_loop, *t_loop; + s_loop = mount_image(source, "/cache/source"); + if (s_loop == NULL) return 1; + t_loop = mount_image(target, "/cache/target"); + if (t_loop == NULL) return 1; + + DIR *dir; + struct dirent *entry; + if (!(dir = xopendir("/cache/source"))) + return 1; + while ((entry = xreaddir(dir))) { + if (entry->d_type == DT_DIR) { + if (strcmp(entry->d_name, ".") == 0 || + strcmp(entry->d_name, "..") == 0 || + strcmp(entry->d_name, ".core") == 0 || + strcmp(entry->d_name, "lost+found") == 0) + continue; + // Cleanup old module + snprintf(buf, PATH_MAX, "/cache/target/%s", entry->d_name); + if (access(buf, F_OK) == 0) { + LOGI("merge module %s\n", entry->d_name); + rm_rf(buf); + } + } + } + closedir(dir); + clone_dir("/cache/source", "/cache/target"); + + // Unmount all loop devices + umount_image("/cache/source", s_loop); + umount_image("/cache/target", t_loop); + rmdir("/cache/source"); + rmdir("/cache/target"); + free(s_loop); + free(t_loop); + unlink(source); + return 0; +} + +static void trim_img(const char *img) { + int used, total, new_size; + get_img_size(img, &used, &total); + new_size = round_size(used); + if (new_size < total) + resize_img(img, new_size); +} + /*********** * Scripts * ***********/ @@ -81,7 +186,7 @@ static char *mount_image(const char *img, const char *target) { void exec_common_script(const char* stage) { DIR *dir; struct dirent *entry; - snprintf(buf, PATH_MAX, "/magisk/.core/%s.d", stage); + snprintf(buf, PATH_MAX, "%s/.core/%s.d", MOUNTPOINT, stage); if (!(dir = opendir(buf))) return; @@ -105,7 +210,7 @@ void exec_common_script(const char* stage) { void exec_module_script(const char* stage) { char *module; vec_for_each(&module_list, module) { - snprintf(buf, PATH_MAX, "/magisk/%s/%s.sh", module, stage); + snprintf(buf, PATH_MAX, "%s/%s/%s.sh", MOUNTPOINT, module, stage); if (access(buf, F_OK) == -1) continue; LOGI("%s: exec [%s.sh]\n", module, stage); @@ -179,7 +284,7 @@ static void construct_tree(const char *module, const char *path, struct node_ent struct dirent *entry; struct node_entry *node; - snprintf(buf, PATH_MAX, "/magisk/%s/%s", module, path); + snprintf(buf, PATH_MAX, "%s/%s/%s", MOUNTPOINT, module, path); if (!(dir = xopendir(buf))) return; @@ -202,14 +307,14 @@ static void construct_tree(const char *module, const char *path, struct node_ent } else { if (entry->d_type == DT_DIR) { // Check if marked as replace - snprintf(buf, PATH_MAX, "/magisk/%s/%s/%s/.replace", module, path, entry->d_name); - if (access(buf, F_OK) == 0) { + snprintf(buf2, PATH_MAX, "%s/%s/%s/%s/.replace", MOUNTPOINT, module, path, entry->d_name); + if (access(buf2, F_OK) == 0) { // Replace everything, mark as leaf node->status = IS_MODULE; } else { // Travel deeper - snprintf(buf, PATH_MAX, "%s/%s", path, entry->d_name); - char *new_path = strdup(buf); + snprintf(buf2, PATH_MAX, "%s/%s", path, entry->d_name); + char *new_path = strdup(buf2); construct_tree(module, new_path, node); free(new_path); } @@ -245,12 +350,12 @@ static void clone_skeleton(struct node_entry *node, const char *real_path) { } closedir(dir); - snprintf(buf, PATH_MAX, "/dev/magisk/dummy%s", real_path); + snprintf(buf, PATH_MAX, "%s%s", DUMMDIR, real_path); xmkdir_p(buf, 0755); bind_mount(buf, real_path); vec_for_each(node->children, child) { - snprintf(buf, PATH_MAX, "/dev/magisk/dummy%s/%s", real_path, child->name); + snprintf(buf, PATH_MAX, "%s%s/%s", DUMMDIR, real_path, child->name); if (child->type == DT_DIR) { xmkdir(buf, 0755); } else if (child->type == DT_REG) { @@ -258,10 +363,10 @@ static void clone_skeleton(struct node_entry *node, const char *real_path) { } if (child->status == IS_MODULE) { // Mount from module file to dummy file - snprintf(buf2, PATH_MAX, "/magisk/%s%s/%s", child->module, real_path, child->name); + snprintf(buf2, PATH_MAX, "%s/%s%s/%s", MOUNTPOINT, child->module, real_path, child->name); } else if (child->status == IS_DUMMY) { // Mount from mirror to dummy file - snprintf(buf2, PATH_MAX, "/dev/magisk/mirror%s/%s", real_path, child->name); + snprintf(buf2, PATH_MAX, "%s%s/%s", MIRRDIR, real_path, child->name); } else if (child->status == IS_SKEL) { // It's another skeleton, recursive call and end char *s = get_full_path(child); @@ -287,8 +392,13 @@ static void magic_mount(struct node_entry *node) { char *real_path; struct node_entry *child; - if (strcmp(node->name, "vendor") == 0 && strcmp(node->parent->name, "/system") == 0) + if (strcmp(node->name, "vendor") == 0 && strcmp(node->parent->name, "/system") == 0) { + snprintf(buf, PATH_MAX, "%s/%s/system/vendor", MOUNTPOINT, node->module); + snprintf(buf2, PATH_MAX, "%s/%s/vendor", MOUNTPOINT, node->module); + unlink(buf2); + symlink(buf, buf2); return; + } if (node->status == DO_NOTHING) { vec_for_each(node->children, child) @@ -296,7 +406,7 @@ static void magic_mount(struct node_entry *node) { } else { real_path = get_full_path(node); if (node->status == IS_MODULE) { - snprintf(buf, PATH_MAX, "/magisk/%s%s", node->module, real_path); + snprintf(buf, PATH_MAX, "%s/%s%s", MOUNTPOINT, node->module, real_path); bind_mount(buf, real_path); } else if (node->status == IS_SKEL) { clone_skeleton(node, real_path); @@ -314,12 +424,23 @@ static void *start_magisk_hide(void *args) { return NULL; } +static void unblock_boot_process() { + close(open(UNBLOCKFILE, O_RDONLY | O_CREAT)); + pthread_exit(NULL); +} + void post_fs(int client) { + // Error handler + err_handler = unblock_boot_process; LOGI("** post-fs mode running\n"); // ack write_int(client, 0); close(client); + // Uninstall or core only mode + if (access(UNINSTALLER, F_OK) == 0 || access(DISABLEFILE, F_OK) == 0) + goto unblock; + // TODO: Simple bind mounts // Allocate buffer @@ -331,32 +452,61 @@ unblock: } void post_fs_data(int client) { + // Error handler + err_handler = unblock_boot_process; // ack write_int(client, 0); close(client); if (!check_data()) goto unblock; + LOGI("** post-fs-data mode running\n"); + + // uninstaller + if (access(UNINSTALLER, F_OK) == 0) { + close(open(UNBLOCKFILE, O_RDONLY | O_CREAT)); + char *const command[] = { "sh", UNBLOCKFILE, NULL }; + run_command(NULL, "/system/bin/sh", command); + return; + } + // Allocate buffer if (buf == NULL) buf = xmalloc(PATH_MAX); if (buf2 == NULL) buf2 = xmalloc(PATH_MAX); - LOGI("** post-fs-data mode running\n"); - LOGI("* Mounting magisk.img\n"); + // Cache support + if (access("/cache/data_bin", F_OK) == 0) { + rm_rf(DATABIN); + rename("/cache/data_bin", DATABIN); + system("mv /cache/stock_boot* /data"); // Lazy.... use bash glob.... + } + + // Merge images + if (merge_img("/cache/magisk.img", MAINIMG)) + goto unblock; + if (merge_img("/data/magisk_merge.img", MAINIMG)) + goto unblock; + + LOGI("* Mounting " MAINIMG "\n"); // Mounting magisk image - char *magiskimg = mount_image("/data/magisk.img", "/magisk"); - free(magiskimg); + char *magiskloop = mount_image(MAINIMG, MOUNTPOINT); + if (magiskloop == NULL) + goto unblock; // Run common scripts LOGI("* Running post-fs-data.d scripts\n"); exec_common_script("post-fs-data"); + // Core only mode + if (access(DISABLEFILE, F_OK) == 0) + goto unblock; + DIR *dir; struct dirent *entry; char *module; struct node_entry *sys_root, *ven_root = NULL, *child; - if (!(dir = xopendir("/magisk"))) + if (!(dir = xopendir(MOUNTPOINT))) goto unblock; // Create the system root entry @@ -375,27 +525,35 @@ void post_fs_data(int client) { strcmp(entry->d_name, ".core") == 0 || strcmp(entry->d_name, "lost+found") == 0) continue; + snprintf(buf, PATH_MAX, "%s/%s", MOUNTPOINT, entry->d_name); + // Check whether remove + snprintf(buf2, PATH_MAX, "%s/remove", buf); + if (access(buf2, F_OK) == 0) { + rm_rf(buf); + continue; + } // Check whether disable - snprintf(buf, PATH_MAX, "/magisk/%s/disable", entry->d_name); - if (access(buf, F_OK) == 0) + snprintf(buf2, PATH_MAX, "%s/disable", buf); + if (access(buf2, F_OK) == 0) continue; // Add the module to list module = strdup(entry->d_name); vec_push_back(&module_list, module); // Read props - snprintf(buf, PATH_MAX, "/magisk/%s/system.prop", module); - if (access(buf, F_OK) == 0) { + snprintf(buf2, PATH_MAX, "%s/system.prop", buf); + if (access(buf2, F_OK) == 0) { LOGI("%s: loading [system.prop]\n", module); - read_prop_file(buf, 0); + read_prop_file(buf2, 0); } // Check whether enable auto_mount - snprintf(buf, PATH_MAX, "/magisk/%s/auto_mount", module); - if (access(buf, F_OK) == -1) + snprintf(buf2, PATH_MAX, "%s/auto_mount", buf); + if (access(buf2, F_OK) == -1) continue; // Double check whether the system folder exists - snprintf(buf, PATH_MAX, "/magisk/%s/system", module); - if (access(buf, F_OK) == -1) + snprintf(buf2, PATH_MAX, "%s/system", buf); + if (access(buf2, F_OK) == -1) continue; + // Construct structure has_modules = 1; LOGI("%s: constructing magic mount structure\n", module); @@ -405,10 +563,18 @@ void post_fs_data(int client) { closedir(dir); + // Trim image + umount_image(MOUNTPOINT, magiskloop); + free(magiskloop); + trim_img(MAINIMG); + + // Remount them back :) + magiskloop = mount_image(MAINIMG, MOUNTPOINT); + free(magiskloop); + if (has_modules) { // Mount mirrors LOGI("* Mounting system/vendor mirrors"); - char block[256]; int seperate_vendor = 0; struct vector mounts; vec_init(&mounts); @@ -416,25 +582,29 @@ void post_fs_data(int client) { char *line; vec_for_each(&mounts, line) { if (strstr(line, " /system ")) { - sscanf(line, "%s", block); - xmkdir_p("/dev/magisk/mirror/system", 0755); - xmount(block, "/dev/magisk/mirror/system", "ext4", MS_RDONLY, NULL); - LOGD("mount: %s -> /dev/magisk/mirror/system\n", block); + sscanf(line, "%s", buf); + snprintf(buf2, PATH_MAX, "%s/system", MIRRDIR); + xmkdir_p(buf2, 0755); + xmount(buf, buf2, "ext4", MS_RDONLY, NULL); + LOGD("mount: %s -> %s\n", buf, buf2); continue; } if (strstr(line, " /vendor ")) { seperate_vendor = 1; - sscanf(line, "%s", block); - xmkdir_p("/dev/magisk/mirror/vendor", 0755); - xmount(block, "/dev/magisk/mirror/vendor", "ext4", MS_RDONLY, NULL); - LOGD("mount: %s -> /dev/magisk/mirror/vendor\n", block); + sscanf(line, "%s", buf); + snprintf(buf2, PATH_MAX, "%s/vendor", MIRRDIR); + xmkdir_p(buf2, 0755); + xmount(buf, buf2, "ext4", MS_RDONLY, NULL); + LOGD("mount: %s -> %s\n", buf, buf2); continue; } } vec_deep_destroy(&mounts); if (!seperate_vendor) { - symlink("/dev/magisk/mirror/system/vendor", "/dev/magisk/mirror/vendor"); - LOGD("link: /dev/magisk/mirror/system/vendor -> /dev/magisk/mirror/vendor\n"); + snprintf(buf, PATH_MAX, "%s/system/vendor", MIRRDIR); + snprintf(buf2, PATH_MAX, "%s/vendor", MIRRDIR); + symlink(buf, buf2); + LOGD("link: %s -> %s\n", buf, buf2); } // Magic!! diff --git a/jni/daemon/log_monitor.c b/jni/daemon/log_monitor.c index 8f7486733..063a0dc96 100644 --- a/jni/daemon/log_monitor.c +++ b/jni/daemon/log_monitor.c @@ -8,7 +8,6 @@ #include #include #include -#include #include "magisk.h" #include "utils.h" @@ -19,8 +18,8 @@ static void *logger_thread(void *args) { err_handler = exit_thread; char buffer[PATH_MAX]; - rename("/cache/magisk.log", "/cache/last_magisk.log"); - FILE *logfile = xfopen("/cache/magisk.log", "w"); + rename(LOGFILE, LASTLOG); + FILE *logfile = xfopen(LOGFILE, "w"); // Disable buffering setbuf(logfile, NULL); // Start logcat @@ -30,7 +29,6 @@ static void *logger_thread(void *args) { while (fdgets(buffer, sizeof(buffer), fd)) { fprintf(logfile, "%s", buffer); } - close(fd); return NULL; } diff --git a/jni/magisk.h b/jni/magisk.h index d3ca765da..ae56f4faf 100644 --- a/jni/magisk.h +++ b/jni/magisk.h @@ -24,6 +24,18 @@ #define ARG_MAX 4096 #endif +#define LOGFILE "/cache/magisk.log" +#define LASTLOG "/cache/last_magisk.log" +#define UNBLOCKFILE "/dev/.magisk.unblock" +#define DISABLEFILE "/cache/.disable_magisk" +#define UNINSTALLER "/cache/magisk_uninstaller.sh" +#define MOUNTPOINT "/magisk" +#define MAINIMG "/data/magisk.img" +#define DATABIN "/data/magisk" +#define MAGISKTMP "/dev/magisk" +#define MIRRDIR MAGISKTMP "/mirror" +#define DUMMDIR MAGISKTMP "/dummy" + #define SELINUX_PATH "/sys/fs/selinux/" #define SELINUX_ENFORCE SELINUX_PATH "enforce" #define SELINUX_POLICY SELINUX_PATH "policy" diff --git a/jni/utils/misc.c b/jni/utils/misc.c index 865cbeab1..349b98748 100644 --- a/jni/utils/misc.c +++ b/jni/utils/misc.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include "magisk.h" #include "utils.h" @@ -204,11 +206,6 @@ void unlock_blocks() { closedir(dir); } -void unblock_boot_process() { - int fd = open("/dev/.magisk.unblock", O_RDONLY | O_CREAT); - close(fd); -} - void setup_sighandlers(void (*handler)(int)) { struct sigaction act; memset(&act, 0, sizeof(act)); @@ -276,3 +273,110 @@ int bind_mount(const char *from, const char *to) { int open_new(const char *filename) { return xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); } + +// file/link -> file/link only!! +int cp_afc(const char *source, const char *target) { + struct stat buf; + xlstat(source, &buf); + unlink(target); + char *con; + if (S_ISREG(buf.st_mode)) { + int sfd, tfd; + sfd = xopen(source, O_RDONLY); + tfd = xopen(target, O_WRONLY | O_CREAT | O_TRUNC); + xsendfile(tfd, sfd, NULL, buf.st_size); + fchmod(tfd, buf.st_mode & 0777); + fchown(tfd, buf.st_uid, buf.st_gid); + fgetfilecon(sfd, &con); + fsetfilecon(tfd, con); + free(con); + close(sfd); + close(tfd); + } else if (S_ISLNK(buf.st_mode)) { + char buffer[PATH_MAX]; + xreadlink(source, buffer, sizeof(buffer)); + xsymlink(buffer, target); + lgetfilecon(source, &con); + lsetfilecon(target, con); + free(con); + } else { + return 1; + } + return 0; +} + +int clone_dir(const char *source, const char *target) { + DIR *dir; + struct dirent *entry; + char *s_path, *t_path, *con; + + if (!(dir = xopendir(source))) + return 1; + + s_path = xmalloc(PATH_MAX); + t_path = xmalloc(PATH_MAX); + + struct stat buf; + xstat(source, &buf); + mkdir_p(target, buf.st_mode & 0777); + xchmod(target, buf.st_mode & 0777); + lgetfilecon(source, &con); + lsetfilecon(target, con); + free(con); + + while ((entry = xreaddir(dir))) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + snprintf(s_path, PATH_MAX, "%s/%s", source, entry->d_name); + snprintf(t_path, PATH_MAX, "%s/%s", target, entry->d_name); + switch (entry->d_type) { + case DT_DIR: + clone_dir(s_path, t_path); + break; + case DT_REG: + case DT_LNK: + cp_afc(s_path, t_path); + break; + } + } + free(s_path); + free(t_path); + + closedir(dir); + return 0; +} + +int rm_rf(const char *target) { + struct stat buf; + xlstat(target, &buf); + char *next; + if (S_ISDIR(buf.st_mode)) { + DIR *dir; + struct dirent *entry; + if (!(dir = xopendir(target))) + return 1; + next = xmalloc(PATH_MAX); + while ((entry = xreaddir(dir))) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + snprintf(next, PATH_MAX, "%s/%s", target, entry->d_name); + switch (entry->d_type) { + case DT_DIR: + rm_rf(next); + break; + case DT_REG: + case DT_LNK: + unlink(next); + break; + } + } + free(next); + closedir(dir); + rmdir(target); + } else if (S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode)) { + unlink(target); + } else { + return 1; + } + return 0; +} diff --git a/jni/utils/utils.h b/jni/utils/utils.h index 6748372b6..e6884dc32 100644 --- a/jni/utils/utils.h +++ b/jni/utils/utils.h @@ -49,12 +49,15 @@ int xpthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); int xsocketpair(int domain, int type, int protocol, int sv[2]); int xstat(const char *pathname, struct stat *buf); +int xlstat(const char *pathname, struct stat *buf); int xdup2(int oldfd, int newfd); ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz); int xsymlink(const char *target, const char *linkpath); int xmount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data); +int xumount(const char *target); +int xumount2(const char *target, int flags); int xchmod(const char *pathname, mode_t mode); int xrename(const char *oldpath, const char *newpath); int xmkdir(const char *pathname, mode_t mode); @@ -77,11 +80,13 @@ void ps(void (*func)(int)); void ps_filter_proc_name(const char *filter, void (*func)(int)); int create_links(const char *bin, const char *path); void unlock_blocks(); -void unblock_boot_process(); void setup_sighandlers(void (*handler)(int)); int run_command(int *fd, const char *path, char *const argv[]); int mkdir_p(const char *pathname, mode_t mode); int bind_mount(const char *from, const char *to); int open_new(const char *filename); +int cp_afc(const char *source, const char *target); +int clone_dir(const char *source, const char *target); +int rm_rf(const char *target); #endif diff --git a/jni/utils/xwrap.c b/jni/utils/xwrap.c index 968e76e6c..33e8b5217 100644 --- a/jni/utils/xwrap.c +++ b/jni/utils/xwrap.c @@ -220,6 +220,14 @@ int xstat(const char *pathname, struct stat *buf) { return ret; } +int xlstat(const char *pathname, struct stat *buf) { + int ret = lstat(pathname, buf); + if (ret == -1) { + PLOGE("lstat %s", pathname); + } + return ret; +} + int xdup2(int oldfd, int newfd) { int ret = dup2(oldfd, newfd); if (ret == -1) { @@ -257,6 +265,22 @@ int xmount(const char *source, const char *target, return ret; } +int xumount(const char *target) { + int ret = umount(target); + if (ret == -1) { + PLOGE("umount %s", target); + } + return ret; +} + +int xumount2(const char *target, int flags) { + int ret = umount2(target, flags); + if (ret == -1) { + PLOGE("umount2 %s", target); + } + return ret; +} + int xchmod(const char *pathname, mode_t mode) { int ret = chmod(pathname, mode); if (ret == -1) {