General.install() runs copy() -> extra1() -> set_perm(). set_perm() only walks
self.files, which are paths under copy_dir (the overlay), so the copy that
Magisk.setup() makes into <data_dir>/adb/magisk keeps the 0644 the binaries
inherit from the apk extraction.
A non-executable /data/adb/magisk/magisk64 makes magisk_env() abort at
post-fs-data. Root still works, because that runs from the overlay copy, but
Zygisk never initialises: the Magisk app reports "Zygisk: No" no matter what the
zygisk setting says, and no error is surfaced to explain why. This breaks every
Zygisk module (LSPosed, Shamiko, PlayIntegrityFix, ...).
Fix setup() to apply the same 0755 to its own destination that
Magisk.set_path_perm() already applies to magisk paths.
Adds a regression test that passes with the fix and fails without it. It needs no
Waydroid install, no root and no extra dependencies:
python3 tests/test_magisk_setup_perms.py
Fixes #283
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
185 lines
7.4 KiB
Python
185 lines
7.4 KiB
Python
import gzip
|
|
import os
|
|
import shutil
|
|
import re
|
|
from stuff.general import General
|
|
from tools.helper import download_file, get_data_dir, host
|
|
from tools.logger import Logger
|
|
from tools import container
|
|
|
|
class Magisk(General):
|
|
id = "magisk delta"
|
|
partition = "system"
|
|
dl_link = "https://github.com/mistrmochov/magiskdeltaorig/raw/main/app-release.apk"
|
|
dl_file_name = "magisk.apk"
|
|
extract_to = "/tmp/magisk_unpack"
|
|
magisk_dir = os.path.join(partition, "etc", "init", "magisk")
|
|
files = ["etc/init/magisk", "etc/init/bootanim.rc"]
|
|
oringinal_bootanim = """
|
|
service bootanim /system/bin/bootanimation
|
|
class core animation
|
|
user graphics
|
|
group graphics audio
|
|
disabled
|
|
oneshot
|
|
ioprio rt 0
|
|
task_profiles MaxPerformance
|
|
|
|
"""
|
|
bootanim_component = f"""
|
|
on post-fs-data
|
|
start logd
|
|
exec u:r:su:s0 root root -- /system/etc/init/magisk/magiskpolicy --live --magisk
|
|
exec u:r:magisk:s0 root root -- /system/etc/init/magisk/magiskpolicy --live --magisk
|
|
exec u:r:update_engine:s0 root root -- /system/etc/init/magisk/magiskpolicy --live --magisk
|
|
mkdir /dev/magisk_iqeoVo2mDrO 700
|
|
exec u:r:su:s0 root root -- /system/etc/init/magisk/magisk64 --auto-selinux --setup-sbin /system/etc/init/magisk /dev/magisk_iqeoVo2mDrO
|
|
exec u:r:su:s0 root root -- /dev/magisk_iqeoVo2mDrO/magisk --auto-selinux --post-fs-data
|
|
|
|
on nonencrypted
|
|
exec u:r:su:s0 root root -- /dev/magisk_iqeoVo2mDrO/magisk --auto-selinux --service
|
|
|
|
on property:vold.decrypt=trigger_restart_framework
|
|
exec u:r:su:s0 root root -- /dev/magisk_iqeoVo2mDrO/magisk --auto-selinux --service
|
|
|
|
on property:sys.boot_completed=1
|
|
mkdir /data/adb/magisk 755
|
|
exec u:r:su:s0 root root -- /dev/magisk_iqeoVo2mDrO/magisk --auto-selinux --boot-complete
|
|
|
|
on property:init.svc.zygote=restarting
|
|
exec u:r:su:s0 root root -- /dev/magisk_iqeoVo2mDrO/magisk --auto-selinux --zygote-restart
|
|
|
|
on property:init.svc.zygote=stopped
|
|
exec u:r:su:s0 root root -- /dev/magisk_iqeoVo2mDrO/magisk --auto-selinux --zygote-restart
|
|
"""
|
|
|
|
def download(self):
|
|
if os.path.isfile(self.download_loc):
|
|
os.remove(self.download_loc)
|
|
Logger.info("Downloading latest Magisk-Delta to {} now ...".format(self.download_loc))
|
|
download_file(self.dl_link, self.download_loc)
|
|
|
|
# require additional setup
|
|
def setup(self):
|
|
Logger.info("Additional setup")
|
|
magisk_absolute_dir = os.path.join(self.copy_dir, self.magisk_dir)
|
|
data_dir = get_data_dir()
|
|
data_magisk_dir = os.path.join(data_dir, "adb", "magisk")
|
|
shutil.copytree(magisk_absolute_dir, data_magisk_dir, dirs_exist_ok=True)
|
|
|
|
# General.install() calls set_perm() only over self.files, which are paths
|
|
# under copy_dir (the overlay). This /data/adb copy is made here, before
|
|
# that runs, so it keeps the 0644 the binaries inherited from the apk
|
|
# extraction. A non-executable /data/adb/magisk/magisk64 makes magisk_env()
|
|
# abort at post-fs-data: root still works from the overlay copy, but Zygisk
|
|
# silently never initialises (Magisk reports "Zygisk: No" no matter what
|
|
# the zygisk setting says). Apply the same perms set_path_perm() gives
|
|
# magisk paths, to this copy too.
|
|
self.set_data_magisk_perm(data_magisk_dir)
|
|
|
|
def set_data_magisk_perm(self, data_magisk_dir):
|
|
if not os.path.isdir(data_magisk_dir):
|
|
return
|
|
os.chmod(data_magisk_dir, 0o755)
|
|
for root, dirnames, filenames in os.walk(data_magisk_dir):
|
|
for dirname in dirnames:
|
|
os.chmod(os.path.join(root, dirname), 0o755)
|
|
for filename in filenames:
|
|
os.chmod(os.path.join(root, filename), 0o755)
|
|
|
|
def copy(self):
|
|
magisk_absolute_dir = os.path.join(self.copy_dir, self.magisk_dir)
|
|
if not os.path.exists(magisk_absolute_dir):
|
|
os.makedirs(magisk_absolute_dir, exist_ok=True)
|
|
|
|
if not os.path.exists(os.path.join(self.copy_dir, "sbin")):
|
|
os.makedirs(os.path.join(self.copy_dir, "sbin"), exist_ok=True)
|
|
|
|
Logger.info("Copying magisk libs now ...")
|
|
|
|
lib_dir = os.path.join(self.extract_to, "lib", self.arch[0])
|
|
for parent, dirnames, filenames in os.walk(lib_dir):
|
|
for filename in filenames:
|
|
o_path = os.path.join(lib_dir, filename)
|
|
filename = re.search('lib(.*)\.so', filename)
|
|
n_path = os.path.join(magisk_absolute_dir, filename.group(1))
|
|
shutil.copyfile(o_path, n_path)
|
|
shutil.copyfile(self.download_loc, os.path.join(magisk_absolute_dir,"magisk.apk") )
|
|
shutil.copytree(os.path.join(self.extract_to, "assets", "chromeos"), os.path.join(magisk_absolute_dir, "chromeos"), dirs_exist_ok=True)
|
|
assets_files = [
|
|
"addon.d.sh",
|
|
"boot_patch.sh",
|
|
"stub.apk",
|
|
"util_functions.sh"
|
|
]
|
|
for f in assets_files:
|
|
shutil.copyfile(os.path.join(self.extract_to, "assets", f), os.path.join(magisk_absolute_dir, f))
|
|
|
|
# Updating Magisk from Magisk manager will modify bootanim.rc,
|
|
# So it is necessary to backup the original bootanim.rc.
|
|
bootanim_path = os.path.join(self.copy_dir, self.partition, "etc", "init", "bootanim.rc")
|
|
gz_filename = os.path.join(bootanim_path)+".gz"
|
|
with gzip.open(gz_filename,'wb') as f_gz:
|
|
f_gz.write(self.oringinal_bootanim.encode('utf-8'))
|
|
with open(bootanim_path, "w") as initfile:
|
|
initfile.write(self.oringinal_bootanim+self.bootanim_component)
|
|
|
|
def set_path_perm(self, path):
|
|
if "magisk" in path.split("/"):
|
|
perms = [0, 2000, 0o755, 0o755]
|
|
else:
|
|
perms = [0, 0, 0o755, 0o644]
|
|
|
|
mode = os.stat(path).st_mode
|
|
|
|
if os.path.isdir(path):
|
|
mode |= perms[2]
|
|
else:
|
|
mode |= perms[3]
|
|
|
|
os.chown(path, perms[0], perms[1])
|
|
os.chmod(path, mode)
|
|
|
|
def extra1(self):
|
|
self.delete_upper()
|
|
self.setup()
|
|
|
|
# Delete the contents of upperdir
|
|
def delete_upper(self):
|
|
if container.use_overlayfs():
|
|
sys_overlay_rw = "/var/lib/waydroid/overlay_rw"
|
|
files = [
|
|
"system/system/etc/init/bootanim.rc",
|
|
"system/system/etc/init/bootanim.rc.gz",
|
|
"system/system/etc/init/magisk",
|
|
"system/system/addon.d/99-magisk.sh",
|
|
"vendor/etc/selinux/precompiled_sepolicy"
|
|
]
|
|
|
|
for f in files:
|
|
file = os.path.join(sys_overlay_rw, f)
|
|
if os.path.isdir(file):
|
|
shutil.rmtree(file)
|
|
elif os.path.isfile(file) or os.path.exists(file):
|
|
os.remove(file)
|
|
|
|
def extra2(self):
|
|
self.delete_upper()
|
|
data_dir = get_data_dir()
|
|
files = [
|
|
os.path.join(data_dir, "adb/magisk.db"),
|
|
os.path.join(data_dir, "adb/magisk")
|
|
]
|
|
for file in files:
|
|
if os.path.isdir(file):
|
|
shutil.rmtree(file)
|
|
elif os.path.isfile(file):
|
|
os.remove(file)
|
|
bootanim_path = os.path.join(self.copy_dir, self.partition, "etc", "init", "bootanim.rc")
|
|
if container.use_overlayfs():
|
|
if os.path.exists(bootanim_path):
|
|
os.remove(bootanim_path)
|
|
else:
|
|
with open(bootanim_path, "w") as initfile:
|
|
initfile.write(self.oringinal_bootanim)
|