Magisk/scripts/util_functions.sh

499 lines
13 KiB
Bash
Raw Normal View History

#########################################
#
2017-06-18 16:15:44 +00:00
# Magisk General Utility Functions
# by topjohnwu
#
#########################################
2017-06-18 16:15:44 +00:00
2019-02-24 07:11:11 +00:00
#MAGISK_VERSION_STUB
2019-02-11 22:14:07 +00:00
###################
# Helper Functions
###################
2017-07-10 17:54:11 +00:00
2019-02-11 22:14:07 +00:00
ui_print() {
$BOOTMODE && echo "$1" || echo -e "ui_print $1\nui_print" >> /proc/self/fd/$OUTFD
}
2018-01-01 08:46:28 +00:00
2019-02-11 22:14:07 +00:00
toupper() {
echo "$@" | tr '[:lower:]' '[:upper:]'
}
2019-02-11 22:14:07 +00:00
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
2019-02-11 22:14:07 +00:00
}
######################
# Environment Related
######################
2017-10-31 09:05:24 +00:00
2018-08-29 02:03:12 +00:00
setup_flashable() {
2018-08-02 20:25:00 +00:00
# Preserve environment varibles
OLD_PATH=$PATH
2019-02-24 07:11:11 +00:00
ensure_bb
$BOOTMODE && return
2018-07-07 09:48:05 +00:00
if [ -z $OUTFD ] || readlink /proc/$$/fd/$OUTFD | grep -q /tmp; then
# We will have to manually find out OUTFD
2018-07-07 09:48:05 +00:00
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
2017-07-09 16:17:34 +00:00
OUTFD=$FD
break
fi
fi
done
fi
}
2019-02-24 07:11:11 +00:00
ensure_bb() {
2019-02-11 22:14:07 +00:00
if [ -x $MAGISKTMP/busybox/busybox ]; then
2019-02-24 07:11:11 +00:00
[ -z $BBDIR ] && BBDIR=$MAGISKTMP/busybox
2019-02-11 22:14:07 +00:00
elif [ -x $TMPDIR/bin/busybox ]; then
2019-02-24 07:11:11 +00:00
[ -z $BBDIR ] && BBDIR=$TMPDIR/bin
2019-02-11 22:14:07 +00:00
else
# Construct the PATH
2019-02-24 07:11:11 +00:00
[ -z $BBDIR ] && BBDIR=$TMPDIR/bin
mkdir -p $BBDIR
ln -s $MAGISKBIN/busybox $BBDIR/busybox
$MAGISKBIN/busybox --install -s $BBDIR
2019-02-11 22:14:07 +00:00
fi
2019-02-24 07:11:11 +00:00
echo $PATH | grep -q "^$BBDIR" || export PATH=$BBDIR:$PATH
2017-06-18 16:15:44 +00:00
}
2019-02-11 22:14:07 +00:00
recovery_actions() {
2019-02-24 07:11:11 +00:00
# Make sure random don't get blocked
2019-02-11 22:14:07 +00:00
mount -o bind /dev/urandom /dev/random
# Unset library paths
OLD_LD_LIB=$LD_LIBRARY_PATH
OLD_LD_PRE=$LD_PRELOAD
2019-02-24 07:11:11 +00:00
OLD_LD_CFG=$LD_CONFIG_FILE
2019-02-11 22:14:07 +00:00
unset LD_LIBRARY_PATH
unset LD_PRELOAD
2019-02-24 07:11:11 +00:00
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
2019-02-11 22:14:07 +00:00
}
recovery_cleanup() {
ui_print "- Unmounting partitions"
umount -l /system 2>/dev/null
umount -l /system_root 2>/dev/null
2019-02-11 22:14:07 +00:00
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
2018-06-20 17:37:08 +00:00
}
2019-02-11 22:14:07 +00:00
#######################
# Installation Related
#######################
2018-06-20 17:37:08 +00:00
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
2018-06-20 17:37:08 +00:00
for uevent in /sys/dev/block/*/uevent; do
local DEVNAME=`grep_prop DEVNAME $uevent`
local PARTNAME=`grep_prop PARTNAME $uevent`
2018-12-24 13:36:37 +00:00
for BLOCK in "$@"; do
if [ "`toupper $BLOCK`" = "`toupper $PARTNAME`" ]; then
2018-06-20 17:37:08 +00:00
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"
}
2017-09-12 20:07:25 +00:00
mount_partitions() {
# Check A/B slot
2018-06-20 17:37:08 +00:00
SLOT=`grep_cmdline androidboot.slot_suffix`
if [ -z $SLOT ]; then
SLOT=`grep_cmdline androidboot.slot`
[ -z $SLOT ] || SLOT=_${SLOT}
2017-11-10 17:33:50 +00:00
fi
2018-06-20 17:37:08 +00:00
[ -z $SLOT ] || ui_print "- Current boot slot: $SLOT"
2018-01-01 08:46:28 +00:00
# Mount ro partitions
mount_ro_ensure system
2019-03-03 11:35:25 +00:00
if [ -f /system/init.rc ]; then
2018-06-17 09:59:24 +00:00
SYSTEM_ROOT=true
[ -L /system_root ] && rm -f /system_root
2017-09-12 20:07:25 +00:00
mkdir /system_root 2>/dev/null
mount --move /system /system_root
mount -o bind /system_root/system /system
else
Logical Resizable Android Partitions support The way how logical partition, or "Logical Resizable Android Partitions" as they say in AOSP source code, is setup makes it impossible to early mount the partitions from the shared super partition with just a few lines of code; in fact, AOSP has a whole "fs_mgr" folder which consist of multiple complex libraries, with 15K lines of code just to deal with the device mapper shenanigans. In order to keep the already overly complicated MagiskInit more managable, I chose NOT to go the route of including fs_mgr directly into MagiskInit. Luckily, starting from Android Q, Google decided to split init startup into 3 stages, with the first stage doing _only_ early mount. This is great news, because we can simply let the stock init do its own thing for us, and we intercept the bootup sequence. So the workflow can be visualized roughly below: Magisk First Stage --> First Stage Mount --> Magisk Second Stage --+ (MagiskInit) (Original Init) (MagiskInit) + + + ...Rest of the boot... <-- Second Stage <-- Selinux Setup <--+ (__________________ Original Init ____________________) The catch here is that after doing all the first stage mounting, /init will pivot /system as root directory (/), leaving us impossible to regain control after we hand it over. So the solution here is to patch fstab in /first_stage_ramdisk on-the-fly to redirect /system to /system_root, making the original init do all the hard work for us and mount required early mount partitions, but skips the step of switching root directory. It will also conveniently hand over execution back to MagiskInit, which we will reuse the routine for patching root directory in normal system-as-root situations.
2019-06-29 07:47:29 +00:00
grep ' / ' /proc/mounts | grep -qv 'rootfs' || grep -q ' /system_root ' /proc/mounts \
&& SYSTEM_ROOT=true || SYSTEM_ROOT=false
2017-09-12 20:07:25 +00:00
fi
[ -L /system/vendor ] && mount_ro_ensure vendor
2019-05-01 05:22:37 +00:00
$SYSTEM_ROOT && ui_print "- Device is system-as-root"
# Mount persist partition 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
2017-09-12 20:07:25 +00:00
}
get_flags() {
# override variables
getvar KEEPVERITY
getvar KEEPFORCEENCRYPT
2018-12-24 17:08:46 +00:00
getvar RECOVERYMODE
if [ -z $KEEPVERITY ]; then
if $SYSTEM_ROOT; then
KEEPVERITY=true
2019-03-03 11:35:25 +00:00
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
2019-03-03 11:35:25 +00:00
ui_print "- Encrypted data, keep forceencrypt"
else
KEEPFORCEENCRYPT=false
fi
fi
2018-12-24 17:08:46 +00:00
[ -z $RECOVERYMODE ] && RECOVERYMODE=false
}
2018-06-20 17:37:08 +00:00
find_boot_image() {
BOOTIMAGE=
if $RECOVERYMODE; then
BOOTIMAGE=`find_block recovery_ramdisk$SLOT recovery`
elif [ ! -z $SLOT ]; then
2018-12-24 13:36:37 +00:00
BOOTIMAGE=`find_block ramdisk$SLOT recovery_ramdisk$SLOT boot$SLOT`
2018-06-20 17:37:08 +00:00
else
BOOTIMAGE=`find_block ramdisk recovery_ramdisk kern-a android_boot kernel boot lnx bootimg boot_a`
2018-06-20 17:37:08 +00:00
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
2018-06-20 17:37:08 +00:00
}
2018-08-10 10:59:14 +00:00
flash_image() {
# Make sure all blocks are writable
$MAGISKBIN/magisk --unlock-blocks 2>/dev/null
2017-09-26 12:21:43 +00:00
case "$1" in
*.gz) CMD1="$MAGISKBIN/magiskboot decompress '$1' - 2>/dev/null";;
2019-02-11 22:14:07 +00:00
*) CMD1="cat '$1'";;
2017-09-26 12:21:43 +00:00
esac
2018-02-09 19:34:13 +00:00
if $BOOTSIGNED; then
2019-02-11 22:14:07 +00:00
CMD2="$BOOTSIGNER -sign"
ui_print "- Sign image with verity keys"
2018-02-09 19:34:13 +00:00
else
2019-02-11 22:14:07 +00:00
CMD2="cat -"
2018-08-10 10:59:14 +00:00
fi
if [ -b "$2" ]; then
2019-02-25 01:39:01 +00:00
local img_sz=`stat -c '%s' "$1"`
local blk_sz=`blockdev --getsize64 "$2"`
[ $img_sz -gt $blk_sz ] && return 1
2019-02-11 22:14:07 +00:00
eval $CMD1 | eval $CMD2 | cat - /dev/zero > "$2" 2>/dev/null
2018-08-10 10:59:14 +00:00
else
ui_print "- Not block device, storing image"
2019-02-11 22:14:07 +00:00
eval $CMD1 | eval $CMD2 > "$2" 2>/dev/null
2018-02-09 19:34:13 +00:00
fi
return 0
2017-09-06 08:13:23 +00:00
}
patch_dtb_partitions() {
local result=1
cd $MAGISKBIN
for name in dtb dtbo; do
local IMAGE=`find_block $name$SLOT`
if [ ! -z $IMAGE ]; then
ui_print "- $name image: $IMAGE"
if ./magiskboot dtb $IMAGE patch dt.patched; then
result=0
ui_print "- Backing up stock $name image"
cat $IMAGE > stock_${name}.img
ui_print "- Flashing patched $name"
cat dt.patched /dev/zero > $IMAGE
rm -f dt.patched
fi
2017-11-10 17:33:50 +00:00
fi
done
cd /
return $result
2017-11-10 17:33:50 +00:00
}
# Common installation script for flash_script.sh and addon.d.sh
install_magisk() {
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
SOURCEDMODE=true
. ./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
patch_dtb_partitions
run_migrations
}
2017-09-06 08:13:23 +00:00
sign_chromeos() {
2017-10-07 14:08:10 +00:00
ui_print "- Signing ChromeOS boot image"
2017-09-06 08:13:23 +00:00
2017-10-07 14:08:10 +00:00
echo > empty
./chromeos/futility vbutil_kernel --pack new-boot.img.signed \
2017-09-06 08:13:23 +00:00
--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
}
2017-06-18 16:15:44 +00:00
remove_system_su() {
if [ -f /system/bin/su -o -f /system/xbin/su ] && [ ! -f /su/bin/su ]; then
2018-08-29 02:03:12 +00:00
ui_print "- Removing system installed root"
2017-06-18 16:15:44 +00:00
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
2017-06-18 16:15:44 +00:00
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
}
2017-07-02 13:36:09 +00:00
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
2018-04-22 06:13:27 +00:00
ARCH32=arm
2017-07-02 13:36:09 +00:00
IS64BIT=false
2018-04-22 06:13:27 +00:00
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;
2017-07-02 13:36:09 +00:00
}
2018-06-26 14:41:03 +00:00
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
2019-02-11 22:14:07 +00:00
$DATA && NVBASE=/data || NVBASE=/cache/data_adb
$DATA_DE && NVBASE=/data/adb
resolve_vars
2018-06-26 14:41:03 +00:00
}
2019-02-11 22:14:07 +00:00
find_manager_apk() {
[ -z $APK ] && APK=/data/adb/magisk.apk
2019-02-11 22:14:07 +00:00
[ -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
2019-11-02 04:41:51 +00:00
[ -f $APK ] || [ -z $DBAPK ] || APK=/data/app/$DBAPK*/*.apk
2017-08-12 08:44:58 +00:00
fi
[ -f $APK ] || ui_print "! Unable to detect Magisk Manager APK for BootSigner"
2017-07-02 13:36:09 +00:00
}
run_migrations() {
local LOCSHA1
local TARGET
# Legacy app installation
local BACKUP=/data/adb/magisk/stock_boot*.gz
if [ -f $BACKUP ]; then
cp $BACKUP /data
rm -f $BACKUP
fi
# Legacy backup
for gz in /data/stock_boot*.gz; do
[ -f $gz ] || break
LOCSHA1=`basename $gz | sed -e 's/stock_boot_//' -e 's/.img.gz//'`
[ -z $LOCSHA1 ] && break
mkdir /data/magisk_backup_${LOCSHA1} 2>/dev/null
mv $gz /data/magisk_backup_${LOCSHA1}/boot.img.gz
done
# Stock backups
LOCSHA1=$SHA1
for name in boot dtb dtbo; do
BACKUP=/data/adb/magisk/stock_${name}.img
[ -f $BACKUP ] || continue
if [ $name = 'boot' ]; then
LOCSHA1=`$MAGISKBIN/magiskboot sha1 $BACKUP`
mkdir /data/magisk_backup_${LOCSHA1} 2>/dev/null
fi
TARGET=/data/magisk_backup_${LOCSHA1}/${name}.img
cp $BACKUP $TARGET
rm -f $BACKUP
gzip -9f $TARGET
done
}
2019-02-11 22:14:07 +00:00
#################
# Module Related
#################
2017-07-09 16:17:34 +00:00
set_perm() {
2018-02-09 19:34:13 +00:00
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
2017-07-09 16:17:34 +00:00
}
set_perm_recursive() {
find $1 -type d 2>/dev/null | while read dir; do
set_perm $dir $2 $3 $4 $6
done
2017-07-30 19:03:52 +00:00
find $1 -type f -o -type l 2>/dev/null | while read file; do
2017-07-09 16:17:34 +00:00
set_perm $file $2 $3 $5 $6
done
}
mktouch() {
2017-07-30 19:03:52 +00:00
mkdir -p ${1%/*} 2>/dev/null
[ -z $2 ] && touch $1 || echo $2 > $1
2017-07-09 16:17:34 +00:00
chmod 644 $1
}
request_size_check() {
reqSizeM=`du -ms "$1" | cut -f1`
2017-07-09 16:17:34 +00:00
}
request_zip_size_check() {
reqSizeM=`unzip -l "$1" | tail -n 1 | awk '{ print int(($1 - 1) / 1048576 + 1) }'`
}
2019-02-24 07:11:11 +00:00
boot_actions() { return; }
2019-02-11 22:14:07 +00:00
##########
# 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
2019-02-11 22:14:07 +00:00
resolve_vars