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));
}
}
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;
}

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -8,11 +8,18 @@
#include <sys/wait.h>
#include <sys/mount.h>
#include <sys/sendfile.h>
#include <sys/statvfs.h>
#include <linux/loop.h>
#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 <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) {
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;
}

View File

@ -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
}