#!/system/bin/sh LOGFILE=/cache/magisk.log IMG=/data/magisk.img MOUNTPOINT=/magisk COREDIR=$MOUNTPOINT/.core TMPDIR=/dev/magisk DUMMDIR=$TMPDIR/dummy MIRRDIR=$TMPDIR/mirror MOUNTINFO=$TMPDIR/mnt # Use the included busybox for maximum compatibility and reliable results # e.g. we rely on the option "-c" for cp (reserve contexts), and -exec for find TOOLPATH=/data/busybox BINPATH=/data/magisk # Legacy support for old phh, we don't change PATH now # Will remove in the next release export OLDPATH=$PATH log_print() { echo "$1" echo "$1" >> $LOGFILE log -p i -t Magisk "$1" } mktouch() { mkdir -p ${1%/*} 2>/dev/null if [ -z "$2" ]; then touch $1 2>/dev/null else echo $2 > $1 2>/dev/null fi } unblock() { touch /dev/.magisk.unblock exit } run_scripts() { BASE=$MOUNTPOINT for MOD in $BASE/* ; do if [ ! -f "$MOD/disable" ]; then if [ -f "$MOD/$1.sh" ]; then chmod 755 $MOD/$1.sh chcon "u:object_r:system_file:s0" "$MOD/$1.sh" log_print "$1: $MOD/$1.sh" sh $MOD/$1.sh fi fi done } loopsetup() { LOOPDEVICE= for DEV in $(ls /dev/block/loop*); do if [ `$TOOLPATH/losetup $DEV $1 >/dev/null 2>&1; echo $?` -eq 0 ]; then LOOPDEVICE=$DEV break fi done } target_size_check() { e2fsck -p -f $1 curBlocks=`e2fsck -n $1 2>/dev/null | cut -d, -f3 | cut -d\ -f2`; curUsedM=$((`echo "$curBlocks" | cut -d/ -f1` * 4 / 1024)); curSizeM=$((`echo "$curBlocks" | cut -d/ -f2` * 4 / 1024)); curFreeM=$((curSizeM - curUsedM)); } travel() { cd "$1/$2" if [ -f ".replace" ]; then rm -rf "$MOUNTINFO/$2" mktouch "$MOUNTINFO/$2" "$1" else for ITEM in * ; do if [ ! -e "/$2/$ITEM" ]; then # New item found if [ "$2" = "system" ]; then # We cannot add new items to /system root, delete it rm -rf "$ITEM" else # If we are in a higher level, delete the lower levels rm -rf "$MOUNTINFO/dummy/$2" # Mount the dummy parent mktouch "$MOUNTINFO/dummy/$2" if [ -d "$ITEM" ]; then # Create new dummy directory and mount it mkdir -p "$DUMMDIR/$2/$ITEM" mktouch "$MOUNTINFO/$2/$ITEM" "$1" elif [ -L "$ITEM" ]; then # Symlinks are small, copy them mkdir -p "$DUMMDIR/$2" 2>/dev/null $TOOLPATH/cp -afc "$ITEM" "$DUMMDIR/$2/$ITEM" else # Create new dummy file and mount it mktouch "$DUMMDIR/$2/$ITEM" mktouch "$MOUNTINFO/$2/$ITEM" "$1" fi fi else if [ -d "$ITEM" ]; then # It's an directory, travel deeper (travel "$1" "$2/$ITEM") elif [ ! -L "$ITEM" ]; then # Mount this file mktouch "$MOUNTINFO/$2/$ITEM" "$1" fi fi done fi } clone_dummy() { for ITEM in "$1/"* ; do if [ -d "$DUMMDIR$ITEM" ]; then (clone_dummy "$ITEM") else if [ -d "$ITEM" ]; then # Create dummy directory mkdir -p "$DUMMDIR$ITEM" elif [ -L "$ITEM" ]; then # Symlinks are small, copy them $TOOLPATH/cp -afc "$ITEM" "$DUMMDIR$ITEM" else # Create dummy file mktouch "$DUMMDIR$ITEM" fi fi done } bind_mount() { if [ -e "$1" -a -e "$2" ]; then mount -o bind $1 $2 if [ "$?" -eq "0" ]; then log_print "Mount: $1" else log_print "Mount Fail: $1" fi fi } merge_image() { if [ -f "$1" ]; then log_print "$1 found" if [ -f "$IMG" ]; then log_print "$IMG found, attempt to merge" # Handle large images target_size_check $1 MERGEUSED=$curUsedM target_size_check $IMG if [ "$MERGEUSED" -gt "$curFreeM" ]; then NEWDATASIZE=$((((MERGEUSED + curUsedM) / 32 + 2) * 32)) log_print "Expanding $IMG to ${NEWDATASIZE}M..." resize2fs $IMG ${NEWDATASIZE}M fi # Start merging mkdir /cache/data_img mkdir /cache/merge_img # setup loop devices loopsetup $IMG LOOPDATA=$LOOPDEVICE log_print "$LOOPDATA $IMG" loopsetup $1 LOOPMERGE=$LOOPDEVICE log_print "$LOOPMERGE $1" if [ ! -z "$LOOPDATA" ]; then if [ ! -z "$LOOPMERGE" ]; then # if loop devices have been setup, mount images OK=true if [ `mount -t ext4 -o rw,noatime $LOOPDATA /cache/data_img >/dev/null 2>&1; echo $?` -ne 0 ]; then OK=false fi if [ `mount -t ext4 -o rw,noatime $LOOPMERGE /cache/merge_img >/dev/null 2>&1; echo $?` -ne 0 ]; then OK=false fi if ($OK); then # Merge (will reserve selinux contexts) cd /cache/merge_img for MOD in *; do if [ "$MOD" != "lost+found" ]; then log_print "Merging: $MOD" rm -rf /cache/data_img/$MOD $TOOLPATH/cp -afc $MOD /cache/data_img/ fi done $TOOLPATH/cp -afc .core/. /cache/data_img/.core 2>/dev/null log_print "Merge complete" fi umount /cache/data_img umount /cache/merge_img fi fi $TOOLPATH/losetup -d $LOOPDATA $TOOLPATH/losetup -d $LOOPMERGE rmdir /cache/data_img rmdir /cache/merge_img else log_print "Moving $1 to $IMG " mv $1 $IMG fi rm -f $1 fi } case $1 in post-fs ) mv $LOGFILE /cache/last_magisk.log touch $LOGFILE chmod 644 $LOGFILE # No more cache mods! # Only for multirom! # log_print "** Magisk post-fs mode running..." unblock ;; post-fs-data ) if [ `mount | grep " /data " >/dev/null 2>&1; echo $?` -ne 0 ]; then # /data not mounted yet, we will be called again later unblock fi if [ `mount | grep " /data " | grep "tmpfs" >/dev/null 2>&1; echo $?` -eq 0 ]; then # /data not mounted yet, we will be called again later unblock fi # Don't run twice if [ "`getprop magisk.restart_pfsd`" != "1" ]; then log_print "** Magisk post-fs-data mode running..." # Live patch sepolicy $BINPATH/sepolicy-inject --live -s su # Multirom functions should go here, not available right now MULTIROM=false # Cache support if [ -d "/cache/data_bin" ]; then rm -rf $BINPATH $TOOLPATH mkdir -p $TOOLPATH mv /cache/data_bin $BINPATH chmod -R 755 $BINPATH $TOOLPATH $BINPATH/busybox --install -s $TOOLPATH ln -s $BINPATH/busybox $TOOLPATH/busybox # Prevent issues rm -f $TOOLPATH/su $TOOLPATH/sh fi mv /cache/stock_boot.img /data 2>/dev/null chcon -R "u:object_r:system_file:s0" $BINPATH $TOOLPATH # Image merging chmod 644 $IMG /cache/magisk.img /data/magisk_merge.img 2>/dev/null merge_image /cache/magisk.img merge_image /data/magisk_merge.img # Mount magisk.img [ ! -d "$MOUNTPOINT" ] && mkdir -p $MOUNTPOINT if [ `cat /proc/mounts | grep $MOUNTPOINT >/dev/null 2>&1; echo $?` -ne 0 ]; then loopsetup $IMG if [ ! -z "$LOOPDEVICE" ]; then mount -t ext4 -o rw,noatime $LOOPDEVICE $MOUNTPOINT fi fi if [ `cat /proc/mounts | grep $MOUNTPOINT >/dev/null 2>&1; echo $?` -ne 0 ]; then log_print "magisk.img mount failed, nothing to do :(" unblock fi # Remove empty directories, legacy paths and symlinks rm -rf $COREDIR/bin $COREDIR/dummy $COREDIR/mirror $TOOLPATH/find $MOUNTPOINT -type d -depth ! -path "*core*" -exec rmdir {} \; 2>/dev/null # Remove modules for MOD in $MOUNTPOINT/* ; do if [ -f "$MOD/remove" ]; then log_print "Remove module: $MOD" rm -rf $MOD fi done # Unmount, shrink, remount if [ `umount $MOUNTPOINT >/dev/null 2>&1; echo $?` -eq 0 ]; then $TOOLPATH/losetup -d $LOOPDEVICE target_size_check $IMG NEWDATASIZE=$(((curUsedM / 32 + 2) * 32)) if [ "$curSizeM" -gt "$NEWDATASIZE" ]; then log_print "Shrinking $IMG to ${NEWDATASIZE}M..." resize2fs $IMG ${NEWDATASIZE}M fi loopsetup $IMG if [ ! -z "$LOOPDEVICE" ]; then mount -t ext4 -o rw,noatime $LOOPDEVICE $MOUNTPOINT fi if [ `cat /proc/mounts | grep $MOUNTPOINT >/dev/null 2>&1; echo $?` -ne 0 ]; then log_print "magisk.img mount failed, nothing to do :(" unblock fi fi log_print "Preparing modules" mkdir -p $DUMMDIR mkdir -p $MIRRDIR/system # Travel through all mods, all magic happens here for MOD in $MOUNTPOINT/* ; do if [ -f "$MOD/auto_mount" -a -d "$MOD/system" -a ! -f "$MOD/disable" ]; then (travel $MOD system) fi done # linker(64), t*box, and app_process* are required if we need to dummy mount bin folder if [ -f "$MOUNTINFO/dummy/system/bin" ]; then rm -f $DUMMDIR/system/bin/linker* $DUMMDIR/system/bin/t*box $DUMMDIR/system/bin/app_process* cd /system/bin $TOOLPATH/cp -afc linker* t*box app_process* $DUMMDIR/system/bin/ fi # Remove crap folder rm -rf $MOUNTPOINT/lost+found # Start doing tasks # Stage 1 log_print "Bind mount dummy system" $TOOLPATH/find $MOUNTINFO/dummy -type f 2>/dev/null | while read ITEM ; do TARGET=${ITEM#$MOUNTINFO/dummy} ORIG="$DUMMDIR$TARGET" clone_dummy "$TARGET" bind_mount "$ORIG" "$TARGET" done # Proper permissions for generated items $TOOLPATH/find $TMPDIR -type d -exec chmod 755 "{}" \; $TOOLPATH/find $TMPDIR -type f -exec chmod 644 "{}" \; $TOOLPATH/find $TMPDIR -exec chcon "u:object_r:system_file:s0" "{}" \; # Stage 2 log_print "Bind mount module items" $TOOLPATH/find $MOUNTINFO/system -type f 2>/dev/null | while read ITEM ; do TARGET=${ITEM#$MOUNTINFO} ORIG=`cat $ITEM`$TARGET bind_mount $ORIG $TARGET rm -f $DUMMDIR${TARGET%/*}/.dummy 2>/dev/null done # Run scripts run_scripts post-fs-data # Bind hosts for Adblock apps [ ! -f "$COREDIR/hosts" ] && $TOOLPATH/cp -afc /system/etc/hosts $COREDIR/hosts log_print "Enabling systemless hosts file support" bind_mount $COREDIR/hosts /system/etc/hosts # Stage 3 log_print "Bind mount system mirror" bind_mount /system $MIRRDIR/system # Stage 4 log_print "Bind mount mirror items" # Find all empty directores and dummy files, they should be mounted by original files in /system TOOLPATH=/data/busybox $TOOLPATH/find $DUMMDIR -type d \ -exec sh -c '[ -z "`$TOOLPATH/ls -A $1`" ] && echo $1' -- {} \; \ -o \( -type f -size 0 -print \) | \ while read ITEM ; do ORIG=${ITEM/dummy/mirror} TARGET=${ITEM#$DUMMDIR} bind_mount $ORIG $TARGET done # Restart post-fs-data if necessary (multirom) ($MULTIROM) && setprop magisk.restart_pfsd 1 fi unblock ;; service ) # Version info MAGISK_VERSION_STUB log_print "** Magisk late_start service mode running..." run_scripts service # Hide Safety Net if [ -f "$COREDIR/magiskhide/enable" ]; then log_print "** Removing tampered read-only system props" VERIFYBOOT=`getprop ro.boot.verifiedbootstate` FLASHLOCKED=`getprop ro.boot.flash.locked` VERITYMODE=`getprop ro.boot.veritymode` [ ! -z "$VERIFYBOOT" -a "$VERIFYBOOT" != "green" ] && log_print "`$BINPATH/resetprop -v -n ro.boot.verifiedbootstate green`" [ ! -z "$FLASHLOCKED" -a "$FLASHLOCKED" != "1" ] && log_print "`$BINPATH/resetprop -v -n ro.boot.flash.locked 1`" [ ! -z "$VERITYMODE" -a "$VERITYMODE" != "enforcing" ] && log_print "`$BINPATH/resetprop -v -n ro.boot.veritymode enforcing`" mktouch $COREDIR/magiskhide/hidelist chmod -R 755 $COREDIR/magiskhide # Add Safety Net preset $COREDIR/magiskhide/add com.google.android.gms.unstable log_print "** Starting Magisk Hide" /data/magisk/magiskhide fi ;; esac