Magisk/scripts/util_functions.sh
topjohnwu b336655a79 Brand new module installer script
The new module installer script completely changes the way how module
installer zips are structured. More info will come later in docs.

The new installer script also supports installing sepolicy.rule to
persist partitions in order to make the module work on the next boot.
2019-12-27 17:53:27 +08:00

474 lines
13 KiB
Bash

#########################################
#
# Magisk General Utility Functions
# by topjohnwu
#
#########################################
#MAGISK_VERSION_STUB
###################
# Helper Functions
###################
ui_print() {
$BOOTMODE && echo "$1" || echo -e "ui_print $1\nui_print" >> /proc/self/fd/$OUTFD
}
toupper() {
echo "$@" | tr '[:lower:]' '[:upper:]'
}
grep_cmdline() {
local REGEX="s/^$1=//p"
cat /proc/cmdline | tr '[:space:]' '\n' | 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 /sbin/.magisk/config /data/.magisk /cache/.magisk`
[ ! -z $VALUE ] && eval $VARNAME=\$VALUE
}
is_mounted() {
grep -q " `readlink -f $1` " /proc/mounts 2>/dev/null
return $?
}
abort() {
ui_print "$1"
$BOOTMODE || recovery_cleanup
exit 1
}
resolve_vars() {
MAGISKBIN=$NVBASE/magisk
POSTFSDATAD=$NVBASE/post-fs-data.d
SERVICED=$NVBASE/service.d
}
######################
# Environment Related
######################
setup_flashable() {
# Preserve environment varibles
OLD_PATH=$PATH
ensure_bb
$BOOTMODE && return
if [ -z $OUTFD ] || readlink /proc/$$/fd/$OUTFD | grep -q /tmp; then
# We will have to manually find out OUTFD
for FD in `ls /proc/$$/fd`; do
if readlink /proc/$$/fd/$FD | grep -q pipe; then
if ps | grep -v grep | grep -q " 3 $FD "; then
OUTFD=$FD
break
fi
fi
done
fi
}
ensure_bb() {
if [ -x $MAGISKTMP/busybox/busybox ]; then
[ -z $BBDIR ] && BBDIR=$MAGISKTMP/busybox
elif [ -x $TMPDIR/bin/busybox ]; then
[ -z $BBDIR ] && BBDIR=$TMPDIR/bin
else
# Construct the PATH
[ -z $BBDIR ] && BBDIR=$TMPDIR/bin
mkdir -p $BBDIR
ln -s $MAGISKBIN/busybox $BBDIR/busybox
$MAGISKBIN/busybox --install -s $BBDIR
fi
echo $PATH | grep -q "^$BBDIR" || export PATH=$BBDIR:$PATH
}
recovery_actions() {
# Make sure random don't get blocked
mount -o bind /dev/urandom /dev/random
# Unset library paths
OLD_LD_LIB=$LD_LIBRARY_PATH
OLD_LD_PRE=$LD_PRELOAD
OLD_LD_CFG=$LD_CONFIG_FILE
unset LD_LIBRARY_PATH
unset LD_PRELOAD
unset LD_CONFIG_FILE
# Force our own busybox path to be in the front
# and do not use anything in recovery's sbin
export PATH=$BBDIR:/system/bin:/vendor/bin
}
recovery_cleanup() {
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
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
[ -z $OLD_LD_CFG ] || export LD_CONFIG_FILE=$OLD_LD_CFG
}
#######################
# Installation Related
#######################
find_block() {
for BLOCK in "$@"; do
DEVICE=`find /dev/block -type l -iname $BLOCK | head -n 1` 2>/dev/null
if [ ! -z $DEVICE ]; then
readlink -f $DEVICE
return 0
fi
done
# Fallback by parsing sysfs uevents
for uevent in /sys/dev/block/*/uevent; do
local DEVNAME=`grep_prop DEVNAME $uevent`
local PARTNAME=`grep_prop PARTNAME $uevent`
for BLOCK in "$@"; do
if [ "`toupper $BLOCK`" = "`toupper $PARTNAME`" ]; then
echo /dev/block/$DEVNAME
return 0
fi
done
done
return 1
}
# mount_name <partname> <mountpoint> <flag>
mount_name() {
local PART=$1
local POINT=$2
local FLAG=$3
[ -L $POINT ] && rm -f $POINT
mkdir -p $POINT 2>/dev/null
is_mounted $POINT && return
ui_print "- Mounting $POINT"
# First try mounting with fstab
mount $FLAG $POINT 2>/dev/null
if ! is_mounted $POINT; then
local BLOCK=`find_block $PART`
mount $FLAG $BLOCK $POINT
fi
}
mount_ro_ensure() {
# We handle ro partitions only in recovery
$BOOTMODE && return
local PART=$1$SLOT
local POINT=/$1
mount_name $PART $POINT '-o ro'
is_mounted $POINT || abort "! Cannot mount $POINT"
}
mount_partitions() {
# Check A/B slot
SLOT=`grep_cmdline androidboot.slot_suffix`
if [ -z $SLOT ]; then
SLOT=`grep_cmdline androidboot.slot`
[ -z $SLOT ] || SLOT=_${SLOT}
fi
[ -z $SLOT ] || ui_print "- Current boot slot: $SLOT"
# Mount ro partitions
mount_ro_ensure system
if [ -f /system/init.rc ]; then
SYSTEM_ROOT=true
[ -L /system_root ] && rm -f /system_root
mkdir /system_root 2>/dev/null
mount --move /system /system_root
mount -o bind /system_root/system /system
else
grep ' / ' /proc/mounts | grep -qv 'rootfs' || grep -q ' /system_root ' /proc/mounts \
&& SYSTEM_ROOT=true || SYSTEM_ROOT=false
fi
[ -L /system/vendor ] && mount_ro_ensure vendor
$SYSTEM_ROOT && ui_print "- Device is system-as-root"
# Persist partitions for module install in recovery
if ! $BOOTMODE && [ ! -z $PERSISTDIR ]; then
# Try to mount persist
PERSISTDIR=/persist
mount_name persist /persist
if ! is_mounted /persist; then
# Fallback to cache
mount_name cache /cache
is_mounted /cache && PERSISTDIR=/cache || PERSISTDIR=
fi
fi
}
get_flags() {
# override variables
getvar KEEPVERITY
getvar KEEPFORCEENCRYPT
getvar RECOVERYMODE
if [ -z $KEEPVERITY ]; then
if $SYSTEM_ROOT; then
KEEPVERITY=true
ui_print "- System-as-root, keep dm/avb-verity"
else
KEEPVERITY=false
fi
fi
if [ -z $KEEPFORCEENCRYPT ]; then
grep ' /data ' /proc/mounts | grep -q 'dm-' && FDE=true || FDE=false
[ -d /data/unencrypted ] && FBE=true || FBE=false
# No data access means unable to decrypt in recovery
if $FDE || $FBE || ! $DATA; then
KEEPFORCEENCRYPT=true
ui_print "- Encrypted data, keep forceencrypt"
else
KEEPFORCEENCRYPT=false
fi
fi
[ -z $RECOVERYMODE ] && RECOVERYMODE=false
}
find_boot_image() {
BOOTIMAGE=
if $RECOVERYMODE; then
BOOTIMAGE=`find_block recovery_ramdisk$SLOT recovery`
elif [ ! -z $SLOT ]; then
BOOTIMAGE=`find_block ramdisk$SLOT recovery_ramdisk$SLOT boot$SLOT`
else
BOOTIMAGE=`find_block ramdisk recovery_ramdisk kern-a android_boot kernel boot lnx bootimg boot_a`
fi
if [ -z $BOOTIMAGE ]; then
# Lets see what fstabs tells me
BOOTIMAGE=`grep -v '#' /etc/*fstab* | grep -E '/boot[^a-zA-Z]' | grep -oE '/dev/[a-zA-Z0-9_./-]*' | head -n 1`
fi
}
flash_image() {
# Make sure all blocks are writable
$MAGISKBIN/magisk --unlock-blocks 2>/dev/null
case "$1" in
*.gz) CMD1="$MAGISKBIN/magiskboot decompress '$1' - 2>/dev/null";;
*) CMD1="cat '$1'";;
esac
if $BOOTSIGNED; then
CMD2="$BOOTSIGNER -sign"
ui_print "- Sign image with verity keys"
else
CMD2="cat -"
fi
if [ -b "$2" ]; then
local img_sz=`stat -c '%s' "$1"`
local blk_sz=`blockdev --getsize64 "$2"`
[ $img_sz -gt $blk_sz ] && return 1
eval $CMD1 | eval $CMD2 | cat - /dev/zero > "$2" 2>/dev/null
else
ui_print "- Not block device, storing image"
eval $CMD1 | eval $CMD2 > "$2" 2>/dev/null
fi
return 0
}
find_dtbo_image() {
DTBOIMAGE=`find_block dtbo$SLOT`
}
patch_dtbo_image() {
find_dtbo_image
if [ ! -z $DTBOIMAGE ]; then
ui_print "- DTBO image: $DTBOIMAGE"
local PATCHED=$TMPDIR/dtbo
if $MAGISKBIN/magiskboot dtb $DTBOIMAGE patch $PATCHED; then
ui_print "- Backing up stock DTBO image"
$MAGISKBIN/magiskboot compress $DTBOIMAGE $MAGISKBIN/stock_dtbo.img.gz
ui_print "- Patching DTBO to remove avb-verity"
cat $PATCHED /dev/zero > $DTBOIMAGE
rm -f $PATCHED
return 0
fi
fi
return 1
}
# Common installation script for flash_script.sh and addon.d.sh
patch_boot_image() {
SOURCEDMODE=true
cd $MAGISKBIN
eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true
$BOOTSIGNED && ui_print "- Boot image is signed with AVB 1.0"
$IS64BIT && mv -f magiskinit64 magiskinit 2>/dev/null || rm -f magiskinit64
# Source the boot patcher
. ./boot_patch.sh "$BOOTIMAGE"
ui_print "- Flashing new boot image"
if ! flash_image new-boot.img "$BOOTIMAGE"; then
ui_print "- Compressing ramdisk to fit in partition"
./magiskboot cpio ramdisk.cpio compress
./magiskboot repack "$BOOTIMAGE"
flash_image new-boot.img "$BOOTIMAGE" || abort "! Insufficient partition size"
fi
./magiskboot cleanup
rm -f new-boot.img
if [ -f stock_boot* ]; then
rm -f /data/stock_boot* 2>/dev/null
$DATA && mv stock_boot* /data
fi
# Patch DTBO together with boot image
$KEEPVERITY || patch_dtbo_image
if [ -f stock_dtbo* ]; then
rm -f /data/stock_dtbo* 2>/dev/null
$DATA && mv stock_dtbo* /data
fi
}
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
}
remove_system_su() {
if [ -f /system/bin/su -o -f /system/xbin/su ] && [ ! -f /su/bin/su ]; then
ui_print "- Removing system installed root"
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
elif [ -e app_process32 ]; then
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;
}
check_data() {
DATA=false
DATA_DE=false
if grep ' /data ' /proc/mounts | grep -vq 'tmpfs'; then
# Test if data is writable
touch /data/.rw && rm /data/.rw && DATA=true
# Test if DE storage is writable
$DATA && [ -d /data/adb ] && touch /data/adb/.rw && rm /data/adb/.rw && DATA_DE=true
fi
$DATA && NVBASE=/data || NVBASE=/cache/data_adb
$DATA_DE && NVBASE=/data/adb
resolve_vars
}
find_manager_apk() {
[ -z $APK ] && APK=/data/adb/magisk.apk
[ -f $APK ] || APK=/data/magisk/magisk.apk
[ -f $APK ] || APK=/data/app/com.topjohnwu.magisk*/*.apk
if [ ! -f $APK ]; then
DBAPK=`magisk --sqlite "SELECT value FROM strings WHERE key='requester'" 2>/dev/null | cut -d= -f2`
[ -z $DBAPK ] && DBAPK=`strings /data/adb/magisk.db | grep 5requester | cut -c11-`
[ -z $DBAPK ] || APK=/data/user_de/*/$DBAPK/dyn/*.apk
[ -f $APK ] || [ -z $DBAPK ] || APK=/data/app/$DBAPK*/*.apk
fi
[ -f $APK ] || ui_print "! Unable to detect Magisk Manager APK for BootSigner"
}
#################
# Module Related
#################
set_perm() {
chown $2:$3 $1 || return 1
chmod $4 $1 || return 1
CON=$5
[ -z $CON ] && CON=u:object_r:system_file:s0
chcon $CON $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) }'`
}
boot_actions() { return; }
##########
# Presets
##########
# Detect whether in boot mode
[ -z $BOOTMODE ] && ps | grep zygote | grep -qv grep && BOOTMODE=true
[ -z $BOOTMODE ] && ps -A 2>/dev/null | grep zygote | grep -qv grep && BOOTMODE=true
[ -z $BOOTMODE ] && BOOTMODE=false
MAGISKTMP=/sbin/.magisk
NVBASE=/data/adb
[ -z $TMPDIR ] && TMPDIR=/dev/tmp
# Bootsigner related stuff
BOOTSIGNERCLASS=a.a
BOOTSIGNER="/system/bin/dalvikvm -Xnodex2oat -Xnoimage-dex2oat -cp \$APK \$BOOTSIGNERCLASS"
BOOTSIGNED=false
resolve_vars