Magisk/scripts/util_functions.sh
topjohnwu 7265450e2e 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.
2018-06-22 06:18:06 +08:00

430 lines
12 KiB
Bash

##########################################################################################
#
# Magisk General Utility Functions
# by topjohnwu
#
# Used everywhere in Magisk
#
##########################################################################################
#MAGISK_VERSION_STUB
# Detect whether in boot mode
ps | grep zygote | grep -v grep >/dev/null && BOOTMODE=true || BOOTMODE=false
$BOOTMODE || ps -A 2>/dev/null | grep zygote | grep -v grep >/dev/null && BOOTMODE=true
$BOOTMODE || id | grep -q 'uid=0' || BOOTMODE=true
# Default location, will override if needed
MAGISKBIN=/data/adb/magisk
[ -z $MOUNTPATH ] && MOUNTPATH=/sbin/.core/img
[ -z $IMG ] && IMG=/data/adb/magisk.img
BOOTSIGNER="/system/bin/dalvikvm -Xnodex2oat -Xnoimage-dex2oat -cp \$APK com.topjohnwu.magisk.utils.BootSigner"
BOOTSIGNED=false
get_outfd() {
if [ $OUTFD -eq 1 ]; then
# We will have to manually find out OUTFD
for FD in `ls /proc/self/fd`; do
if readlink /proc/self/fd/$FD | grep -q pipe; then
if ps | grep -v grep | grep -q " 3 $FD "; then
OUTFD=$FD
break
fi
fi
done
fi
}
ui_print() {
$BOOTMODE && echo "$1" || echo -e "ui_print $1\nui_print" >> /proc/self/fd/$OUTFD
}
toupper() {
echo "$@" | tr '[:lower:]' '[:upper:]'
}
find_block() {
for uevent in /sys/dev/block/*/uevent; do
local DEVNAME=`grep_prop DEVNAME $uevent`
local PARTNAME=`grep_prop PARTNAME $uevent`
for p in "$@"; do
if [ "`toupper $p`" = "`toupper $PARTNAME`" ]; then
echo /dev/block/$DEVNAME
return 0
fi
done
done
return 1
}
mount_partitions() {
# Check A/B slot
SLOT=`grep_cmdline androidboot.slot_suffix`
if [ -z $SLOT ]; then
SLOT=_`grep_cmdline androidboot.slot`
[ $SLOT = "_" ] && SLOT=
fi
[ -z $SLOT ] || ui_print "- Current boot slot: $SLOT"
ui_print "- Mounting /system, /vendor"
[ -f /system/build.prop ] || is_mounted /system || mount -o ro /system 2>/dev/null
if ! is_mounted /system && ! [ -f /system/build.prop ]; then
SYSTEMBLOCK=`find_block system$SLOT`
mount -t ext4 -o ro $SYSTEMBLOCK /system
fi
[ -f /system/build.prop ] || is_mounted /system || abort "! Cannot mount /system"
cat /proc/mounts | grep -E '/dev/root|/system_root' >/dev/null && SYSTEM_ROOT=true || SYSTEM_ROOT=false
if [ -f /system/init ]; then
SYSTEM_ROOT=true
mkdir /system_root 2>/dev/null
mount --move /system /system_root
mount -o bind /system_root/system /system
fi
$SYSTEM_ROOT && ui_print "- Device using system_root_image"
if [ -L /system/vendor ]; then
# Seperate /vendor partition
is_mounted /vendor || mount -o ro /vendor 2>/dev/null
if ! is_mounted /vendor; then
VENDORBLOCK=`find_block vendor$SLOT`
mount -t ext4 -o ro $VENDORBLOCK /vendor
fi
is_mounted /vendor || abort "! Cannot mount /vendor"
fi
}
get_flags() {
# override variables
getvar KEEPVERITY
getvar KEEPFORCEENCRYPT
HIGHCOMP=false
if [ -z $KEEPVERITY ]; then
KEEPVERITY=false
hardware=`grep_cmdline androidboot.hardware`
for hw in taimen walleye; do
if [ "$hw" = "$hardware" ]; then
KEEPVERITY=true
ui_print "- Device on whitelist, keep avb-verity"
break
fi
done
fi
if [ -z $KEEPFORCEENCRYPT ]; then
if [ "`getprop ro.crypto.state`" = "encrypted" ]; then
KEEPFORCEENCRYPT=true
ui_print "- Encrypted data detected, keep forceencrypt"
else
KEEPFORCEENCRYPT=false
fi
fi
}
grep_cmdline() {
local REGEX="s/^$1=//p"
sed -E 's/ +/\n/g' /proc/cmdline | sed -n "$REGEX" 2>/dev/null
}
grep_prop() {
local REGEX="s/^$1=//p"
shift
local FILES=$@
[ -z "$FILES" ] && FILES='/system/build.prop'
sed -n "$REGEX" $FILES 2>/dev/null | head -n 1
}
getvar() {
local VARNAME=$1
local VALUE=
VALUE=`grep_prop $VARNAME /.backup/.magisk /data/.magisk /cache/.magisk /system/.magisk`
[ ! -z $VALUE ] && eval $VARNAME=\$VALUE
}
run_migrations() {
# Update the broken boot backup
if [ -f /data/stock_boot_.img.gz ]; then
$MAGISKBIN/magiskboot --decompress /data/stock_boot_.img.gz /data/stock_boot.img
fi
# Update our previous backup to new format if exists
if [ -f /data/stock_boot.img ]; then
ui_print "- Migrating boot image backup"
SHA1=`$MAGISKBIN/magiskboot --sha1 /data/stock_boot.img 2>/dev/null`
STOCKDUMP=/data/stock_boot_${SHA1}.img
mv /data/stock_boot.img $STOCKDUMP
$MAGISKBIN/magiskboot --compress $STOCKDUMP
fi
# Move the stock backups
if [ -f /data/magisk/stock_boot* ]; then
rm -rf /data/stock_boot*
mv /data/magisk/stock_boot* /data 2>/dev/null
fi
if [ -f /data/adb/magisk/stock_boot* ]; then
rm -rf /data/stock_boot*
mv /data/adb/magisk/stock_boot* /data 2>/dev/null
fi
# Remove old dbs
rm -f /data/user*/*/magisk.db
[ -L /data/magisk.img ] || mv /data/magisk.img /data/adb/magisk.img 2>/dev/null
}
find_boot_image() {
BOOTIMAGE=
if [ ! -z $SLOT ]; then
BOOTIMAGE=`find_block boot$SLOT ramdisk$SLOT`
else
BOOTIMAGE=`find_block boot_a kern-a android_boot kernel boot lnx bootimg`
fi
}
flash_boot_image() {
# Make sure all blocks are writable
$MAGISKBIN/magisk --unlock-blocks 2>/dev/null
case "$1" in
*.gz) COMMAND="gzip -d < '$1'";;
*) COMMAND="cat '$1'";;
esac
if $BOOTSIGNED; then
SIGNCOM="$BOOTSIGNER -sign"
ui_print "- Sign boot image with test keys"
else
SIGNCOM="cat -"
fi
case "$2" in
/dev/block/*)
ui_print "- Flashing new boot image"
eval $COMMAND | eval $SIGNCOM | cat - /dev/zero 2>/dev/null | dd of="$2" bs=4096 2>/dev/null
;;
*)
ui_print "- Storing new boot image"
eval $COMMAND | eval $SIGNCOM | dd of="$2" bs=4096 2>/dev/null
;;
esac
}
find_dtbo_image() {
DTBOIMAGE=`find_block dtbo$SLOT`
}
patch_dtbo_image() {
if [ ! -z $DTBOIMAGE ]; then
if $MAGISKBIN/magiskboot --dtb-test $DTBOIMAGE; then
ui_print "- Backing up stock dtbo image"
$MAGISKBIN/magiskboot --compress $DTBOIMAGE $MAGISKBIN/stock_dtbo.img.gz
ui_print "- Patching fstab in dtbo to remove avb-verity"
$MAGISKBIN/magiskboot --dtb-patch $DTBOIMAGE
return 0
fi
fi
return 1
}
restore_imgs() {
STOCKBOOT=/data/stock_boot_${1}.img.gz
STOCKDTBO=/data/stock_dtbo.img.gz
# Make sure all blocks are writable
$MAGISKBIN/magisk --unlock-blocks 2>/dev/null
find_dtbo_image
if [ ! -z "$DTBOIMAGE" -a -f "$STOCKDTBO" ]; then
ui_print "- Restoring stock dtbo image"
gzip -d < $STOCKDTBO | dd of=$DTBOIMAGE
fi
BOOTSIGNED=false
find_boot_image
if [ ! -z "$BOOTIMAGE" -a -f "$STOCKBOOT" ]; then
ui_print "- Restoring stock boot image"
gzip -d < $STOCKBOOT | cat - /dev/zero 2>/dev/null | dd of="$BOOTIMAGE" bs=4096 2>/dev/null
return 0
fi
return 1
}
sign_chromeos() {
ui_print "- Signing ChromeOS boot image"
echo > empty
./chromeos/futility vbutil_kernel --pack new-boot.img.signed \
--keyblock ./chromeos/kernel.keyblock --signprivate ./chromeos/kernel_data_key.vbprivk \
--version 1 --vmlinuz new-boot.img --config empty --arch arm --bootloader empty --flags 0x1
rm -f empty new-boot.img
mv new-boot.img.signed new-boot.img
}
is_mounted() {
cat /proc/mounts | grep -q " `readlink -f $1` " 2>/dev/null
return $?
}
remove_system_su() {
if [ -f /system/bin/su -o -f /system/xbin/su ] && [ ! -f /su/bin/su ]; then
ui_print "! System installed root detected, mount rw :("
mount -o rw,remount /system
# SuperSU
if [ -e /system/bin/.ext/.su ]; then
mv -f /system/bin/app_process32_original /system/bin/app_process32 2>/dev/null
mv -f /system/bin/app_process64_original /system/bin/app_process64 2>/dev/null
mv -f /system/bin/install-recovery_original.sh /system/bin/install-recovery.sh 2>/dev/null
cd /system/bin
if [ -e app_process64 ]; then
ln -sf app_process64 app_process
else
ln -sf app_process32 app_process
fi
fi
rm -rf /system/.pin /system/bin/.ext /system/etc/.installed_su_daemon /system/etc/.has_su_daemon \
/system/xbin/daemonsu /system/xbin/su /system/xbin/sugote /system/xbin/sugote-mksh /system/xbin/supolicy \
/system/bin/app_process_init /system/bin/su /cache/su /system/lib/libsupol.so /system/lib64/libsupol.so \
/system/su.d /system/etc/install-recovery.sh /system/etc/init.d/99SuperSUDaemon /cache/install-recovery.sh \
/system/.supersu /cache/.supersu /data/.supersu \
/system/app/Superuser.apk /system/app/SuperSU /cache/Superuser.apk 2>/dev/null
fi
}
api_level_arch_detect() {
API=`grep_prop ro.build.version.sdk`
ABI=`grep_prop ro.product.cpu.abi | cut -c-3`
ABI2=`grep_prop ro.product.cpu.abi2 | cut -c-3`
ABILONG=`grep_prop ro.product.cpu.abi`
ARCH=arm
ARCH32=arm
IS64BIT=false
if [ "$ABI" = "x86" ]; then ARCH=x86; ARCH32=x86; fi;
if [ "$ABI2" = "x86" ]; then ARCH=x86; ARCH32=x86; fi;
if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; ARCH32=arm; IS64BIT=true; fi;
if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; ARCH32=x86; IS64BIT=true; fi;
}
setup_bb() {
if [ -x /sbin/.core/busybox/busybox ]; then
# Make sure this path is in the front
echo $PATH | grep -q '^/sbin/.core/busybox' || export PATH=/sbin/.core/busybox:$PATH
elif [ -x $TMPDIR/bin/busybox ]; then
# Make sure this path is in the front
echo $PATH | grep -q "^$TMPDIR/bin" || export PATH=$TMPDIR/bin:$PATH
else
# Construct the PATH
mkdir -p $TMPDIR/bin
ln -s $MAGISKBIN/busybox $TMPDIR/bin/busybox
$MAGISKBIN/busybox --install -s $TMPDIR/bin
export PATH=$TMPDIR/bin:$PATH
fi
}
boot_actions() {
if [ ! -d /sbin/.core/mirror/bin ]; then
mkdir -p /sbin/.core/mirror/bin
mount -o bind $MAGISKBIN /sbin/.core/mirror/bin
fi
MAGISKBIN=/sbin/.core/mirror/bin
setup_bb
}
recovery_actions() {
# TWRP bug fix
mount -o bind /dev/urandom /dev/random
# Preserve environment varibles
OLD_PATH=$PATH
setup_bb
# Temporarily block out all custom recovery binaries/libs
mv /sbin /sbin_tmp
# Unset library paths
OLD_LD_LIB=$LD_LIBRARY_PATH
OLD_LD_PRE=$LD_PRELOAD
unset LD_LIBRARY_PATH
unset LD_PRELOAD
}
recovery_cleanup() {
mv /sbin_tmp /sbin 2>/dev/null
[ -z $OLD_PATH ] || export PATH=$OLD_PATH
[ -z $OLD_LD_LIB ] || export LD_LIBRARY_PATH=$OLD_LD_LIB
[ -z $OLD_LD_PRE ] || export LD_PRELOAD=$OLD_LD_PRE
ui_print "- Unmounting partitions"
umount -l /system_root 2>/dev/null
umount -l /system 2>/dev/null
umount -l /vendor 2>/dev/null
umount -l /dev/random 2>/dev/null
}
abort() {
ui_print "$1"
$BOOTMODE || recovery_cleanup
exit 1
}
set_perm() {
chown $2:$3 $1 || return 1
chmod $4 $1 || return 1
[ -z $5 ] && chcon 'u:object_r:system_file:s0' $1 || chcon $5 $1 || return 1
}
set_perm_recursive() {
find $1 -type d 2>/dev/null | while read dir; do
set_perm $dir $2 $3 $4 $6
done
find $1 -type f -o -type l 2>/dev/null | while read file; do
set_perm $file $2 $3 $5 $6
done
}
mktouch() {
mkdir -p ${1%/*} 2>/dev/null
[ -z $2 ] && touch $1 || echo $2 > $1
chmod 644 $1
}
request_size_check() {
reqSizeM=`du -ms $1 | cut -f1`
}
request_zip_size_check() {
reqSizeM=`unzip -l "$1" | tail -n 1 | awk '{ print int(($1 - 1) / 1048576 + 1) }'`
}
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"
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 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))
ui_print "- Creating $IMG with size ${newSizeM}M"
$MAGISKBIN/magisk imgtool create $IMG $newSizeM >&2
mount_snippet
fi
}
unmount_magisk_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 imgtool resize $IMG $newSizeM >&2
fi
}