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.
This commit is contained in:
topjohnwu 2018-06-22 06:18:06 +08:00
parent 058dbc9f9e
commit 7265450e2e
6 changed files with 175 additions and 173 deletions

View File

@ -441,17 +441,10 @@ static int prepare_img() {
vec_push_back(&module_list, strdup(entry->d_name)); vec_push_back(&module_list, strdup(entry->d_name));
} }
} }
closedir(dir); closedir(dir);
// Trim image if (trim_img(MAINIMG, MOUNTPOINT, magiskloop))
umount_image(MOUNTPOINT, magiskloop); return 1;
free(magiskloop);
trim_img(MAINIMG);
// Remount them back :)
magiskloop = mount_image(MAINIMG, MOUNTPOINT);
free(magiskloop);
return 0; return 0;
} }

View File

@ -12,7 +12,8 @@
char *argv0; 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) { int create_links(const char *bin, const char *path) {
char self[PATH_MAX], linkpath[PATH_MAX]; char self[PATH_MAX], linkpath[PATH_MAX];
@ -42,11 +43,6 @@ static void usage() {
" -V print running daemon version code\n" " -V print running daemon version code\n"
" --list list all available applets\n" " --list list all available applets\n"
" --install [SOURCE] DIR symlink all applets to DIR. SOURCE is optional\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" " --daemon manually start magisk daemon\n"
" --[init trigger] start service for init trigger\n" " --[init trigger] start service for init trigger\n"
" --unlock-blocks set BLKROSET flag to OFF for all block devices\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) for (int i = 0; applet[i]; ++i)
printf("%s\n", applet[i]); printf("%s\n", applet[i]);
return 0; 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) { } else if (strcmp(argv[1], "--unlock-blocks") == 0) {
unlock_blocks(); unlock_blocks();
return 0; return 0;

View File

@ -54,7 +54,7 @@
extern char *argv0; /* For changing process name */ 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 }) #define init_applet ((char *[]) { "magiskpolicy", "supolicy", NULL })
extern int (*applet_main[]) (int, char *[]), (*init_applet_main[]) (int, char *[]); 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 magiskpolicy_main(int argc, char *argv[]);
int su_client_main(int argc, char *argv[]); int su_client_main(int argc, char *argv[]);
int resetprop_main(int argc, char *argv[]); int resetprop_main(int argc, char *argv[]);
int imgtool_main(int argc, char *argv[]);
#endif #endif

View File

@ -134,16 +134,15 @@ void write_zero(int fd, size_t size);
// img.c // img.c
#define round_size(a) ((((a) / 32) + 2) * 32) #define round_size(a) ((((a) / 32) + 2) * 32)
#define SOURCE_TMP "/dev/source" #define SOURCE_TMP "/dev/.img_src"
#define TARGET_TMP "/dev/target" #define TARGET_TMP "/dev/.img_tgt"
int create_img(const char *img, int size); 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 enforce);
int resize_img(const char *img, int size);
char *mount_image(const char *img, const char *target); 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); 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 // pattern.c

View File

@ -8,11 +8,18 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/sendfile.h> #include <sys/sendfile.h>
#include <sys/statvfs.h>
#include <linux/loop.h> #include <linux/loop.h>
#include "magisk.h" #include "magisk.h"
#include "utils.h" #include "utils.h"
struct fs_info {
unsigned size;
unsigned free;
unsigned used;
};
static int e2fsck(const char *img) { static int e2fsck(const char *img) {
// Check and repair ext4 image // Check and repair ext4 image
char buffer[128]; char buffer[128];
@ -51,59 +58,86 @@ static char *loopsetup(const char *img) {
return strdup(device); 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 <action> [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) { int create_img(const char *img, int size) {
if (size == 128) /* WTF...? */ if (size == 128) /* WTF...? */
size = 132; size = 132;
unlink(img); unlink(img);
LOGI("Create %s with size %dM\n", img, size); LOGI("Create %s with size %dM\n", img, size);
int ret;
char size_str[16]; char size_str[16];
snprintf(size_str, sizeof(size_str), "%dM", size); snprintf(size_str, sizeof(size_str), "%dM", size);
ret = exec_command_sync("/system/bin/make_ext4fs", "-b", "4096", "-l", size_str, img, NULL); if (access("/system/bin/make_ext4fs", X_OK) == 0)
if (ret < 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 // 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 exec_command_sync("/system/bin/mke2fs", "-b", "4096", "-t", "ext4", img, size_str, NULL);
} else
return ret; return 1;
} }
int get_img_size(const char *img, int *used, int *total) { int resize_img(const char *img, int size, int enforce) {
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) {
LOGI("Resize %s to %dM\n", img, size); LOGI("Resize %s to %dM\n", img, size);
if (e2fsck(img)) if (e2fsck(img))
return 1; return 1;
char buffer[128]; char buffer[128];
int pid, fd = -1, used, total; int pid, fd = -1;
snprintf(buffer, sizeof(buffer), "%dM", size); snprintf(buffer, sizeof(buffer), "%dM", size);
pid = exec_command(1, &fd, NULL, "/system/bin/resize2fs", img, buffer, NULL); pid = exec_command(1, &fd, NULL, "/system/bin/resize2fs", img, buffer, NULL);
if (pid < 0) if (pid < 0)
@ -113,18 +147,21 @@ int resize_img(const char *img, int size) {
close(fd); close(fd);
waitpid(pid, NULL, 0); waitpid(pid, NULL, 0);
// Double check our image size if (enforce) {
get_img_size(img, &used, &total); // Check the image size
if (total != size) { struct stat st;
stat(img, &st);
if (st.st_size / 1048576 != size) {
// Sammy crap occurs or resize2fs failed, lets create a new image! // Sammy crap occurs or resize2fs failed, lets create a new image!
char *dir = dirname(img); snprintf(buffer, sizeof(buffer), "%s/tmp.img", dirname(img));
snprintf(buffer, sizeof(buffer), "%s/tmp.img", dir);
create_img(buffer, size); create_img(buffer, size);
char *s_loop, *t_loop; char *s_loop, *t_loop;
s_loop = mount_image(img, SOURCE_TMP); s_loop = mount_image(img, SOURCE_TMP);
if (s_loop == NULL) return 1; if (s_loop == NULL)
return 1;
t_loop = mount_image(buffer, TARGET_TMP); t_loop = mount_image(buffer, TARGET_TMP);
if (t_loop == NULL) return 1; if (t_loop == NULL)
return 1;
cp_afc(SOURCE_TMP, TARGET_TMP); cp_afc(SOURCE_TMP, TARGET_TMP);
umount_image(SOURCE_TMP, s_loop); umount_image(SOURCE_TMP, s_loop);
@ -135,7 +172,7 @@ int resize_img(const char *img, int size) {
free(t_loop); free(t_loop);
rename(buffer, img); rename(buffer, img);
} }
}
return 0; return 0;
} }
@ -152,11 +189,13 @@ char *mount_image(const char *img, const char *target) {
return device; return device;
} }
void umount_image(const char *target, const char *device) { int umount_image(const char *target, const char *device) {
xumount(target); int ret = 0;
ret |= xumount(target);
int fd = xopen(device, O_RDWR); int fd = xopen(device, O_RDWR);
ioctl(fd, LOOP_CLR_FD); ret |= ioctl(fd, LOOP_CLR_FD);
close(fd); close(fd);
return ret;
} }
int merge_img(const char *source, const char *target) { 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]; 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(SOURCE_TMP, 0755);
xmkdir(TARGET_TMP, 0755); xmkdir(TARGET_TMP, 0755);
char *s_loop, *t_loop; char *s_loop, *t_loop;
s_loop = mount_image(source, SOURCE_TMP); 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); 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; DIR *dir;
struct dirent *entry; struct dirent *entry;
if (!(dir = xopendir(SOURCE_TMP))) if (!(dir = xopendir(SOURCE_TMP)))
@ -207,12 +254,9 @@ int merge_img(const char *source, const char *target) {
continue; continue;
// Cleanup old module if exists // Cleanup old module if exists
snprintf(buffer, sizeof(buffer), "%s/%s", TARGET_TMP, entry->d_name); snprintf(buffer, sizeof(buffer), "%s/%s", TARGET_TMP, entry->d_name);
if (access(buffer, F_OK) == 0) { if (access(buffer, F_OK) == 0)
LOGI("Upgrade module: %s\n", entry->d_name);
rm_rf(buffer); rm_rf(buffer);
} else { LOGI("Upgrade/New module: %s\n", entry->d_name);
LOGI("New module: %s\n", entry->d_name);
}
} }
} }
closedir(dir); closedir(dir);
@ -229,10 +273,18 @@ int merge_img(const char *source, const char *target) {
return 0; return 0;
} }
void trim_img(const char *img) { int trim_img(const char *img, const char *mount, char *loop) {
int used, total, new_size; struct fs_info info;
get_img_size(img, &used, &total); check_filesystem(&info, img, mount);
new_size = round_size(used); int new_size = round_size(info.used);
if (new_size != total) if (info.size > new_size) {
resize_img(img, 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;
} }

View File

@ -375,52 +375,55 @@ mktouch() {
} }
request_size_check() { request_size_check() {
reqSizeM=`du -s $1 | cut -f1` reqSizeM=`du -ms $1 | cut -f1`
reqSizeM=$((reqSizeM / 1024 + 1))
} }
request_zip_size_check() { 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() { check_filesystem() {
SIZE="`$MAGISKBIN/magisk --imgsize $IMG`" curSizeM=`wc -c < $1`
curUsedM=`echo "$SIZE" | cut -d" " -f1` curSizeM=$((curSizeM / 1048576))
curSizeM=`echo "$SIZE" | cut -d" " -f2` local DF=`df -P $2 | grep $2`
curFreeM=$((curSizeM - curUsedM)) 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() { mount_magisk_img() {
[ -z reqSizeM ] && reqSizeM=0 [ -z reqSizeM ] && reqSizeM=0
mkdir -p $MOUNTPATH 2>/dev/null
if [ -f "$IMG" ]; then if [ -f "$IMG" ]; then
ui_print "- Found $IMG" ui_print "- Found $IMG"
image_size_check $IMG mount_snippet
if [ "$reqSizeM" -gt "$curFreeM" ]; then check_filesystem $IMG $MOUNTPATH
newSizeM=$(((reqSizeM + curUsedM) / 32 * 32 + 64)) if [ $reqSizeM -gt $curFreeM ]; then
newSizeM=$(((curSizeM + reqSizeM - curFreeM) / 32 * 32 + 64))
ui_print "- Resizing $IMG to ${newSizeM}M" 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 fi
ui_print "- Mount $IMG to $MOUNTPATH"
else else
newSizeM=$((reqSizeM / 32 * 32 + 64)); newSizeM=$((reqSizeM / 32 * 32 + 64))
ui_print "- Creating $IMG with size ${newSizeM}M" ui_print "- Creating $IMG with size ${newSizeM}M"
$MAGISKBIN/magisk --createimg $IMG $newSizeM >&2 $MAGISKBIN/magisk imgtool create $IMG $newSizeM >&2
mount_snippet
fi 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() { unmount_magisk_img() {
$MAGISKBIN/magisk --umountimg $MOUNTPATH $MAGISKLOOP check_filesystem $IMG $MOUNTPATH
# Shrink the image if possible
image_size_check $IMG
newSizeM=$((curUsedM / 32 * 32 + 64)) newSizeM=$((curUsedM / 32 * 32 + 64))
$MAGISKBIN/magisk imgtool umount $MOUNTPATH $MAGISKLOOP
if [ $curSizeM -gt $newSizeM ]; then if [ $curSizeM -gt $newSizeM ]; then
ui_print "- Shrinking $IMG to ${newSizeM}M" ui_print "- Shrinking $IMG to ${newSizeM}M"
$MAGISKBIN/magisk --resizeimg $IMG $newSizeM $MAGISKBIN/magisk imgtool resize $IMG $newSizeM >&2
fi fi
} }