From 7265450e2ecfb5b2d9ab378efdb7eac91227b2c3 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 22 Jun 2018 06:18:06 +0800 Subject: [PATCH] Precise free space calculation for magisk.img 1. Introduce new applet: imgtool for better separation from the main program 2. Actually mount the image and check statvfs for free space in the image This shall eliminate any possible module installation failure from image resizing issues. --- native/jni/core/bootstages.c | 11 +- native/jni/core/magisk.c | 50 +------- native/jni/include/magisk.h | 3 +- native/jni/include/utils.h | 11 +- native/jni/utils/img.c | 220 ++++++++++++++++++++++------------- scripts/util_functions.sh | 53 +++++---- 6 files changed, 175 insertions(+), 173 deletions(-) diff --git a/native/jni/core/bootstages.c b/native/jni/core/bootstages.c index 7a74c4cf9..bd0ad3838 100644 --- a/native/jni/core/bootstages.c +++ b/native/jni/core/bootstages.c @@ -441,17 +441,10 @@ static int prepare_img() { vec_push_back(&module_list, strdup(entry->d_name)); } } - closedir(dir); - // Trim image - umount_image(MOUNTPOINT, magiskloop); - free(magiskloop); - trim_img(MAINIMG); - - // Remount them back :) - magiskloop = mount_image(MAINIMG, MOUNTPOINT); - free(magiskloop); + if (trim_img(MAINIMG, MOUNTPOINT, magiskloop)) + return 1; return 0; } diff --git a/native/jni/core/magisk.c b/native/jni/core/magisk.c index cc0470967..926f3b32f 100644 --- a/native/jni/core/magisk.c +++ b/native/jni/core/magisk.c @@ -12,7 +12,8 @@ char *argv0; -int (*applet_main[]) (int, char *[]) = { su_client_main, resetprop_main, magiskhide_main, NULL }; +int (*applet_main[]) (int, char *[]) = + { su_client_main, resetprop_main, magiskhide_main, imgtool_main, NULL }; int create_links(const char *bin, const char *path) { char self[PATH_MAX], linkpath[PATH_MAX]; @@ -42,11 +43,6 @@ static void usage() { " -V print running daemon version code\n" " --list list all available applets\n" " --install [SOURCE] DIR symlink all applets to DIR. SOURCE is optional\n" - " --createimg IMG SIZE create ext4 image. SIZE is interpreted in MB\n" - " --imgsize IMG report ext4 image used/total size\n" - " --resizeimg IMG SIZE resize ext4 image. SIZE is interpreted in MB\n" - " --mountimg IMG PATH mount IMG to PATH and prints the loop device\n" - " --umountimg PATH LOOP unmount PATH and delete LOOP device\n" " --daemon manually start magisk daemon\n" " --[init trigger] start service for init trigger\n" " --unlock-blocks set BLKROSET flag to OFF for all block devices\n" @@ -90,48 +86,6 @@ int magisk_main(int argc, char *argv[]) { for (int i = 0; applet[i]; ++i) printf("%s\n", applet[i]); return 0; - } else if (strcmp(argv[1], "--createimg") == 0) { - if (argc < 4) usage(); - int size; - sscanf(argv[3], "%d", &size); - return create_img(argv[2], size); - } else if (strcmp(argv[1], "--imgsize") == 0) { - if (argc < 3) usage(); - int used, total; - if (get_img_size(argv[2], &used, &total)) { - fprintf(stderr, "Cannot check %s size\n", argv[2]); - return 1; - } - printf("%d %d\n", used, total); - return 0; - } else if (strcmp(argv[1], "--resizeimg") == 0) { - if (argc < 4) usage(); - int used, total, size; - sscanf(argv[3], "%d", &size); - if (get_img_size(argv[2], &used, &total)) { - fprintf(stderr, "Cannot check %s size\n", argv[2]); - return 1; - } - if (size <= used) { - fprintf(stderr, "Cannot resize smaller than %dM\n", used); - return 1; - } - return resize_img(argv[2], size); - } else if (strcmp(argv[1], "--mountimg") == 0) { - if (argc < 4) usage(); - char *loop = mount_image(argv[2], argv[3]); - if (loop == NULL) { - fprintf(stderr, "Cannot mount image!\n"); - return 1; - } else { - printf("%s\n", loop); - free(loop); - return 0; - } - } else if (strcmp(argv[1], "--umountimg") == 0) { - if (argc < 4) usage(); - umount_image(argv[2], argv[3]); - return 0; } else if (strcmp(argv[1], "--unlock-blocks") == 0) { unlock_blocks(); return 0; diff --git a/native/jni/include/magisk.h b/native/jni/include/magisk.h index 7607858e5..15356746f 100644 --- a/native/jni/include/magisk.h +++ b/native/jni/include/magisk.h @@ -54,7 +54,7 @@ extern char *argv0; /* For changing process name */ -#define applet ((char *[]) { "su", "resetprop", "magiskhide", NULL }) +#define applet ((char *[]) { "su", "resetprop", "magiskhide", "imgtool", NULL }) #define init_applet ((char *[]) { "magiskpolicy", "supolicy", NULL }) extern int (*applet_main[]) (int, char *[]), (*init_applet_main[]) (int, char *[]); @@ -66,5 +66,6 @@ int magiskhide_main(int argc, char *argv[]); int magiskpolicy_main(int argc, char *argv[]); int su_client_main(int argc, char *argv[]); int resetprop_main(int argc, char *argv[]); +int imgtool_main(int argc, char *argv[]); #endif diff --git a/native/jni/include/utils.h b/native/jni/include/utils.h index 8a2c86285..c43242b15 100644 --- a/native/jni/include/utils.h +++ b/native/jni/include/utils.h @@ -134,16 +134,15 @@ void write_zero(int fd, size_t size); // img.c #define round_size(a) ((((a) / 32) + 2) * 32) -#define SOURCE_TMP "/dev/source" -#define TARGET_TMP "/dev/target" +#define SOURCE_TMP "/dev/.img_src" +#define TARGET_TMP "/dev/.img_tgt" int create_img(const char *img, int size); -int get_img_size(const char *img, int *used, int *total); -int resize_img(const char *img, int size); +int resize_img(const char *img, int size, int enforce); char *mount_image(const char *img, const char *target); -void umount_image(const char *target, const char *device); +int umount_image(const char *target, const char *device); int merge_img(const char *source, const char *target); -void trim_img(const char *img); +int trim_img(const char *img, const char *mount, char *loop); // pattern.c diff --git a/native/jni/utils/img.c b/native/jni/utils/img.c index 418dd91d7..0c69bed51 100644 --- a/native/jni/utils/img.c +++ b/native/jni/utils/img.c @@ -8,11 +8,18 @@ #include #include #include +#include #include #include "magisk.h" #include "utils.h" +struct fs_info { + unsigned size; + unsigned free; + unsigned used; +}; + static int e2fsck(const char *img) { // Check and repair ext4 image char buffer[128]; @@ -51,59 +58,86 @@ static char *loopsetup(const char *img) { return strdup(device); } +static void check_filesystem(struct fs_info *info, const char *img, const char *mount) { + struct stat st; + struct statvfs vfs; + stat(img, &st); + statvfs(mount, &vfs); + info->size = st.st_size / 1048576; + info->free = vfs.f_bfree * vfs.f_frsize / 1048576; + info->used = (vfs.f_blocks - vfs.f_bfree) * vfs.f_frsize / 1048576; +} + +static void usage() { + fprintf(stderr, + "ImgTool v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) - EXT4 Image Tools\n" + "\n" + "Usage: imgtool [args...]\n" + "\n" + "Actions:\n" + " create IMG SIZE create ext4 image. SIZE is interpreted in MB\n" + " resize IMG SIZE resize ext4 image. SIZE is interpreted in MB\n" + " mount IMG PATH mount IMG to PATH and prints the loop device\n" + " umount PATH LOOP unmount PATH and delete LOOP device\n" + ); + exit(1); +} + +int imgtool_main(int argc, char *argv[]) { + if (argc < 2) + usage(); + if (strcmp(argv[1], "create") == 0) { + if (argc < 4) + usage(); + return create_img(argv[2], atoi(argv[3])); + } else if (strcmp(argv[1], "resize") == 0) { + if (argc < 4) + usage(); + return resize_img(argv[2], atoi(argv[3]), 1); + } else if (strcmp(argv[1], "mount") == 0) { + if (argc < 4) + usage(); + char *loop = mount_image(argv[2], argv[3]); + if (loop == NULL) { + fprintf(stderr, "Cannot mount image!\n"); + return 1; + } else { + printf("%s\n", loop); + free(loop); + return 0; + } + } else if (strcmp(argv[1], "umount") == 0) { + if (argc < 4) + usage(); + umount_image(argv[2], argv[3]); + return 0; + } + usage(); + return 1; +} + int create_img(const char *img, int size) { if (size == 128) /* WTF...? */ size = 132; unlink(img); LOGI("Create %s with size %dM\n", img, size); - int ret; - char size_str[16]; snprintf(size_str, sizeof(size_str), "%dM", size); - ret = exec_command_sync("/system/bin/make_ext4fs", "-b", "4096", "-l", size_str, img, NULL); - if (ret < 0) { + if (access("/system/bin/make_ext4fs", X_OK) == 0) + return exec_command_sync("/system/bin/make_ext4fs", "-b", "4096", "-l", size_str, img, NULL); + else if (access("/system/bin/mke2fs", X_OK) == 0) // On Android P there is no make_ext4fs, use mke2fs - ret = exec_command_sync("/system/bin/mke2fs", "-b", "4096", "-t", "ext4", img, size_str, NULL); - } - return ret; + return exec_command_sync("/system/bin/mke2fs", "-b", "4096", "-t", "ext4", img, size_str, NULL); + else + return 1; } -int get_img_size(const char *img, int *used, int *total) { - if (access(img, R_OK) == -1) - return 1; - char buffer[PATH_MAX]; - int pid, fd = -1, status = 1; - pid = exec_command(1, &fd, NULL, "/system/bin/e2fsck", "-n", img, NULL); - if (pid < 0) - return 1; - while (fdgets(buffer, sizeof(buffer), fd)) { - if (strstr(buffer, img)) { - char *tok = strtok(buffer, ","); - while(tok != NULL) { - if (strstr(tok, "blocks")) { - status = 0; - break; - } - tok = strtok(NULL, ","); - } - if (status) continue; - sscanf(tok, "%d/%d", used, total); - *used = (*used + 255) / 256; - *total = (*total + 128) / 256; - break; - } - } - close(fd); - waitpid(pid, NULL, 0); - return 0; -} - -int resize_img(const char *img, int size) { +int resize_img(const char *img, int size, int enforce) { LOGI("Resize %s to %dM\n", img, size); if (e2fsck(img)) return 1; char buffer[128]; - int pid, fd = -1, used, total; + int pid, fd = -1; snprintf(buffer, sizeof(buffer), "%dM", size); pid = exec_command(1, &fd, NULL, "/system/bin/resize2fs", img, buffer, NULL); if (pid < 0) @@ -113,29 +147,32 @@ int resize_img(const char *img, int size) { close(fd); waitpid(pid, NULL, 0); - // Double check our image size - get_img_size(img, &used, &total); - if (total != size) { - // Sammy crap occurs or resize2fs failed, lets create a new image! - char *dir = dirname(img); - snprintf(buffer, sizeof(buffer), "%s/tmp.img", dir); - create_img(buffer, size); - char *s_loop, *t_loop; - s_loop = mount_image(img, SOURCE_TMP); - if (s_loop == NULL) return 1; - t_loop = mount_image(buffer, TARGET_TMP); - if (t_loop == NULL) return 1; + if (enforce) { + // Check the image size + struct stat st; + stat(img, &st); + if (st.st_size / 1048576 != size) { + // Sammy crap occurs or resize2fs failed, lets create a new image! + snprintf(buffer, sizeof(buffer), "%s/tmp.img", dirname(img)); + create_img(buffer, size); + char *s_loop, *t_loop; + s_loop = mount_image(img, SOURCE_TMP); + if (s_loop == NULL) + return 1; + t_loop = mount_image(buffer, TARGET_TMP); + if (t_loop == NULL) + return 1; - cp_afc(SOURCE_TMP, TARGET_TMP); - umount_image(SOURCE_TMP, s_loop); - umount_image(TARGET_TMP, t_loop); - rmdir(SOURCE_TMP); - rmdir(TARGET_TMP); - free(s_loop); - free(t_loop); - rename(buffer, img); + cp_afc(SOURCE_TMP, TARGET_TMP); + umount_image(SOURCE_TMP, s_loop); + umount_image(TARGET_TMP, t_loop); + rmdir(SOURCE_TMP); + rmdir(TARGET_TMP); + free(s_loop); + free(t_loop); + rename(buffer, img); + } } - return 0; } @@ -152,11 +189,13 @@ char *mount_image(const char *img, const char *target) { return device; } -void umount_image(const char *target, const char *device) { - xumount(target); +int umount_image(const char *target, const char *device) { + int ret = 0; + ret |= xumount(target); int fd = xopen(device, O_RDWR); - ioctl(fd, LOOP_CLR_FD); + ret |= ioctl(fd, LOOP_CLR_FD); close(fd); + return ret; } int merge_img(const char *source, const char *target) { @@ -178,22 +217,30 @@ int merge_img(const char *source, const char *target) { char buffer[PATH_MAX]; - // 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); - xmkdir(SOURCE_TMP, 0755); xmkdir(TARGET_TMP, 0755); char *s_loop, *t_loop; s_loop = mount_image(source, SOURCE_TMP); - if (s_loop == NULL) return 1; + if (s_loop == NULL) + return 1; t_loop = mount_image(target, TARGET_TMP); - if (t_loop == NULL) return 1; + if (t_loop == NULL) + return 1; + struct fs_info src, tgt; + check_filesystem(&src, source, SOURCE_TMP); + check_filesystem(&tgt, target, TARGET_TMP); + + // resize target to worst case + if (src.used >= tgt.free) { + umount_image(TARGET_TMP, t_loop); + free(t_loop); + resize_img(target, round_size(tgt.size + src.used - tgt.free), 1); + t_loop = mount_image(target, TARGET_TMP); + } + + snprintf(buffer, sizeof(buffer), "%s/%s", TARGET_TMP, "lost+found"); + rm_rf(buffer); DIR *dir; struct dirent *entry; if (!(dir = xopendir(SOURCE_TMP))) @@ -207,12 +254,9 @@ int merge_img(const char *source, const char *target) { continue; // Cleanup old module if exists snprintf(buffer, sizeof(buffer), "%s/%s", TARGET_TMP, entry->d_name); - if (access(buffer, F_OK) == 0) { - LOGI("Upgrade module: %s\n", entry->d_name); + if (access(buffer, F_OK) == 0) rm_rf(buffer); - } else { - LOGI("New module: %s\n", entry->d_name); - } + LOGI("Upgrade/New module: %s\n", entry->d_name); } } closedir(dir); @@ -229,10 +273,18 @@ int merge_img(const char *source, const char *target) { return 0; } -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); +int trim_img(const char *img, const char *mount, char *loop) { + struct fs_info info; + check_filesystem(&info, img, mount); + int new_size = round_size(info.used); + if (info.size > new_size) { + umount_image(mount, loop); + free(loop); + resize_img(img, new_size, 0); + loop = mount_image(img, mount); + if (loop == NULL) + return 1; + } + free(loop); + return 0; } diff --git a/scripts/util_functions.sh b/scripts/util_functions.sh index 276f88e76..d0d739572 100644 --- a/scripts/util_functions.sh +++ b/scripts/util_functions.sh @@ -375,52 +375,55 @@ mktouch() { } request_size_check() { - reqSizeM=`du -s $1 | cut -f1` - reqSizeM=$((reqSizeM / 1024 + 1)) + reqSizeM=`du -ms $1 | cut -f1` } request_zip_size_check() { - reqSizeM=`unzip -l "$1" | tail -n 1 | awk '{ print int($1 / 1048567 + 1) }'` + reqSizeM=`unzip -l "$1" | tail -n 1 | awk '{ print int(($1 - 1) / 1048576 + 1) }'` } -image_size_check() { - SIZE="`$MAGISKBIN/magisk --imgsize $IMG`" - curUsedM=`echo "$SIZE" | cut -d" " -f1` - curSizeM=`echo "$SIZE" | cut -d" " -f2` - curFreeM=$((curSizeM - curUsedM)) +check_filesystem() { + curSizeM=`wc -c < $1` + curSizeM=$((curSizeM / 1048576)) + local DF=`df -P $2 | grep $2` + curUsedM=`echo $DF | awk '{ print int($3 / 1024) }'` + curFreeM=`echo $DF | awk '{ print int($4 / 1024) }'` +} + +mount_snippet() { + MAGISKLOOP=`$MAGISKBIN/magisk imgtool mount $IMG $MOUNTPATH` + is_mounted $MOUNTPATH || abort "! $IMG mount failed..." } mount_magisk_img() { [ -z reqSizeM ] && reqSizeM=0 + mkdir -p $MOUNTPATH 2>/dev/null if [ -f "$IMG" ]; then ui_print "- Found $IMG" - image_size_check $IMG - if [ "$reqSizeM" -gt "$curFreeM" ]; then - newSizeM=$(((reqSizeM + curUsedM) / 32 * 32 + 64)) + mount_snippet + check_filesystem $IMG $MOUNTPATH + if [ $reqSizeM -gt $curFreeM ]; then + newSizeM=$(((curSizeM + reqSizeM - curFreeM) / 32 * 32 + 64)) ui_print "- Resizing $IMG to ${newSizeM}M" - $MAGISKBIN/magisk --resizeimg $IMG $newSizeM >&2 + $MAGISKBIN/magisk imgtool umount $MOUNTPATH $MAGISKLOOP + $MAGISKBIN/magisk imgtool resize $IMG $newSizeM >&2 + mount_snippet fi + ui_print "- Mount $IMG to $MOUNTPATH" else - newSizeM=$((reqSizeM / 32 * 32 + 64)); + newSizeM=$((reqSizeM / 32 * 32 + 64)) ui_print "- Creating $IMG with size ${newSizeM}M" - $MAGISKBIN/magisk --createimg $IMG $newSizeM >&2 + $MAGISKBIN/magisk imgtool create $IMG $newSizeM >&2 + mount_snippet fi - - ui_print "- Mounting $IMG to $MOUNTPATH" - mkdir -p $MOUNTPATH 2>/dev/null - MAGISKLOOP=`$MAGISKBIN/magisk --mountimg $IMG $MOUNTPATH` - is_mounted $MOUNTPATH || abort "! $IMG mount failed..." } unmount_magisk_img() { - $MAGISKBIN/magisk --umountimg $MOUNTPATH $MAGISKLOOP - - # Shrink the image if possible - image_size_check $IMG + check_filesystem $IMG $MOUNTPATH newSizeM=$((curUsedM / 32 * 32 + 64)) + $MAGISKBIN/magisk imgtool umount $MOUNTPATH $MAGISKLOOP if [ $curSizeM -gt $newSizeM ]; then ui_print "- Shrinking $IMG to ${newSizeM}M" - $MAGISKBIN/magisk --resizeimg $IMG $newSizeM + $MAGISKBIN/magisk imgtool resize $IMG $newSizeM >&2 fi } -