diff --git a/app/src/main/java/com/topjohnwu/magisk/core/model/module/LocalModule.kt b/app/src/main/java/com/topjohnwu/magisk/core/model/module/LocalModule.kt index c48fad7fc..a69ec0a9e 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/model/module/LocalModule.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/model/module/LocalModule.kt @@ -26,11 +26,17 @@ class LocalModule(path: String) : Module() { set(enable) { val dir = "$PERSIST/$id" if (enable) { - Shell.su("mkdir -p $dir", "cp -af $ruleFile $dir").submit() disableFile.delete() + if (Const.Version.isCanary()) + Shell.su("copy_sepolicy_rules").submit() + else + Shell.su("mkdir -p $dir", "cp -af $ruleFile $dir").submit() } else { - Shell.su("rm -rf $dir").submit() !disableFile.createNewFile() + if (Const.Version.isCanary()) + Shell.su("copy_sepolicy_rules").submit() + else + Shell.su("rm -rf $dir").submit() } } @@ -38,11 +44,17 @@ class LocalModule(path: String) : Module() { get() = removeFile.exists() set(remove) { if (remove) { - Shell.su("rm -rf $PERSIST/$id").submit() removeFile.createNewFile() + if (Const.Version.isCanary()) + Shell.su("copy_sepolicy_rules").submit() + else + Shell.su("rm -rf $PERSIST/$id").submit() } else { - Shell.su("cp -af $ruleFile $PERSIST/$id").submit() !removeFile.delete() + if (Const.Version.isCanary()) + Shell.su("copy_sepolicy_rules").submit() + else + Shell.su("cp -af $ruleFile $PERSIST/$id").submit() } } diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp index 421689caa..3837cb686 100644 --- a/native/jni/core/bootstages.cpp +++ b/native/jni/core/bootstages.cpp @@ -22,14 +22,15 @@ static bool safe_mode = false; * Setup * *********/ -#define DIR_IS(part) (me->mnt_dir == "/" #part ""sv) +#define MNT_DIR_IS(dir) (me->mnt_dir == string_view(dir)) #define SETMIR(b, part) sprintf(b, "%s/" MIRRDIR "/" #part, MAGISKTMP.data()) #define SETBLK(b, part) sprintf(b, "%s/" BLOCKDIR "/" #part, MAGISKTMP.data()) #define mount_mirror(part, flag) \ -else if (DIR_IS(part) && me->mnt_type != "tmpfs"sv && lstat(me->mnt_dir, &st) == 0) { \ +else if (MNT_DIR_IS("/" #part) && me->mnt_type != "tmpfs"sv && lstat(me->mnt_dir, &st) == 0) { \ SETMIR(buf1, part); \ SETBLK(buf2, part); \ + unlink(buf2); \ mknod(buf2, S_IFBLK | 0600, st.st_dev); \ xmkdir(buf1, 0755); \ xmount(buf2, buf1, me->mnt_type, flag, nullptr); \ @@ -43,6 +44,16 @@ if (access("/system/" #part, F_OK) == 0 && access(buf1, F_OK) != 0) { \ LOGI("link: %s\n", buf1); \ } +#define link_orig_dir(dir, part) \ +else if (MNT_DIR_IS(dir) && me->mnt_type != "tmpfs"sv) { \ + SETMIR(buf1, part); \ + rmdir(buf1); \ + xsymlink(dir, buf1); \ + LOGI("link: %s\n", buf1); \ +} + +#define link_orig(part) link_orig_dir("/" #part, part) + static bool magisk_env() { LOGI("* Initializing Magisk environment\n"); @@ -98,7 +109,11 @@ static bool magisk_env() { mount_mirror(product, MS_RDONLY) mount_mirror(system_ext, MS_RDONLY) mount_mirror(data, 0) - else if (SDK_INT >= 24 && DIR_IS(proc) && !strstr(me->mnt_opts, "hidepid=2")) { + link_orig(cache) + link_orig(metadata) + link_orig(persist) + link_orig_dir("/mnt/vendor/persist", persist) + else if (SDK_INT >= 24 && MNT_DIR_IS("/proc") && !strstr(me->mnt_opts, "hidepid=2")) { xmount(nullptr, "/proc", nullptr, MS_REMOUNT, "hidepid=2,gid=3009"); } return true; @@ -109,9 +124,9 @@ static bool magisk_env() { xsymlink("./system_root/system", buf1); LOGI("link: %s\n", buf1); } - link_mirror(vendor); - link_mirror(product); - link_mirror(system_ext); + link_mirror(vendor) + link_mirror(product) + link_mirror(system_ext) // Disable/remove magiskhide, resetprop if (SDK_INT < 19) { diff --git a/native/jni/include/magisk.hpp b/native/jni/include/magisk.hpp index 2203f966b..a86688edb 100644 --- a/native/jni/include/magisk.hpp +++ b/native/jni/include/magisk.hpp @@ -17,6 +17,7 @@ extern std::string MAGISKTMP; #define INTLROOT ".magisk" #define MIRRDIR INTLROOT "/mirror" +#define RULESDIR MIRRDIR "/sepolicy.rules" #define BLOCKDIR INTLROOT "/block" #define MODULEMNT INTLROOT "/modules" #define BBPATH INTLROOT "/busybox" diff --git a/native/jni/init/init.hpp b/native/jni/init/init.hpp index 8459f2702..81fe1bc73 100644 --- a/native/jni/init/init.hpp +++ b/native/jni/init/init.hpp @@ -68,11 +68,12 @@ class MagiskInit : public BaseInit { protected: auto_data self; auto_data config; - std::string persist_dir; + std::string custom_rules_dir; void mount_with_dt(); bool patch_sepolicy(const char *file); void setup_tmp(const char *path); + void mount_rules_dir(const char *dev_base, const char *mnt_base); public: MagiskInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {} }; @@ -114,10 +115,11 @@ public: class SecondStageInit : public SARBase { protected: void early_mount() override; - void cleanup() override { /* Do not do any cleanup */ } public: SecondStageInit(char *argv[]) : SARBase(argv, nullptr) { LOGD("%s\n", __FUNCTION__); + // Do not unmount /sys and /proc + mount_list.clear(); }; }; diff --git a/native/jni/init/mount.cpp b/native/jni/init/mount.cpp index dce678a9d..dd665102e 100644 --- a/native/jni/init/mount.cpp +++ b/native/jni/init/mount.cpp @@ -205,27 +205,89 @@ static void switch_root(const string &path) { frm_rf(root); } -static void mount_persist(const char *dev_base, const char *mnt_base) { - string mnt_point = mnt_base + "/persist"s; - strcpy(blk_info.partname, "persist"); +void MagiskInit::mount_rules_dir(const char *dev_base, const char *mnt_base) { + char path[128]; xrealpath(dev_base, blk_info.block_dev); - char *s = blk_info.block_dev + strlen(blk_info.block_dev); - strcpy(s, "/persist"); + xrealpath(mnt_base, path); + char *b = blk_info.block_dev + strlen(blk_info.block_dev); + char *p = path + strlen(path); + + auto do_mount = [&](const char *type) -> bool { + xmkdir(path, 0755); + bool success = xmount(blk_info.block_dev, path, type, 0, nullptr) == 0; + if (success) + mount_list.emplace_back(path); + return success; + }; + + // First try userdata + strcpy(blk_info.partname, "userdata"); + strcpy(b, "/data"); + strcpy(p, "/data"); if (setup_block(false) < 0) { - // Fallback to cache - strcpy(blk_info.partname, "cache"); - strcpy(s, "/cache"); - if (setup_block(false) < 0) { - // Try NVIDIA's BS - strcpy(blk_info.partname, "CAC"); - if (setup_block(false) < 0) - return; - } - xsymlink("./cache", mnt_point.data()); - mnt_point = mnt_base + "/cache"s; + // Try NVIDIA naming scheme + strcpy(blk_info.partname, "UDA"); + if (setup_block(false) < 0) + goto cache; } - xmkdir(mnt_point.data(), 0755); - xmount(blk_info.block_dev, mnt_point.data(), "ext4", 0, nullptr); + // Try to mount with either ext4 or f2fs + // Failure means either FDE or metadata encryption + if (!do_mount("ext4") && !do_mount("f2fs")) + goto cache; + + strcpy(p, "/data/unencrypted"); + if (access(path, F_OK) == 0) { + // FBE, need to use an unencrypted path + custom_rules_dir = path + "/magisk"s; + } else { + // Skip if /data/adb does not exist + strcpy(p, "/data/adb"); + if (access(path, F_OK) != 0) + return; + // Unencrypted, directly use module paths + custom_rules_dir = string(mnt_base) + MODULEROOT; + } + goto success; + +cache: + // Fallback to cache + strcpy(blk_info.partname, "cache"); + strcpy(b, "/cache"); + strcpy(p, "/cache"); + if (setup_block(false) < 0) { + // Try NVIDIA naming scheme + strcpy(blk_info.partname, "CAC"); + if (setup_block(false) < 0) + goto metadata; + } + if (!do_mount("ext4")) + goto metadata; + custom_rules_dir = path + "/magisk"s; + goto success; + +metadata: + // Fallback to metadata + strcpy(blk_info.partname, "metadata"); + strcpy(b, "/metadata"); + strcpy(p, "/metadata"); + if (setup_block(false) < 0 || !do_mount("ext4")) + goto persist; + custom_rules_dir = path + "/magisk"s; + goto success; + +persist: + // Fallback to persist + strcpy(blk_info.partname, "persist"); + strcpy(b, "/persist"); + strcpy(p, "/persist"); + if (setup_block(false) < 0 || !do_mount("ext4")) + return; + custom_rules_dir = path + "/magisk"s; + +success: + // Create symlinks so we don't need to go through this logic again + strcpy(p, "/sepolicy.rules"); + xsymlink(custom_rules_dir.data(), path); } void RootFSInit::early_mount() { @@ -235,11 +297,6 @@ void RootFSInit::early_mount() { rename("/.backup/init", "/init"); mount_with_dt(); - - xmkdir("/dev/mnt", 0755); - mount_persist("/dev/block", "/dev/mnt"); - mount_list.emplace_back("/dev/mnt/persist"); - persist_dir = "/dev/mnt/persist/magisk"; } void SARBase::backup_files() { @@ -337,8 +394,6 @@ void MagiskInit::setup_tmp(const char *path) { xmkdir(MIRRDIR, 0); xmkdir(BLOCKDIR, 0); - mount_persist(BLOCKDIR, MIRRDIR); - int fd = xopen(INTLROOT "/config", O_WRONLY | O_CREAT, 0); xwrite(fd, config.buf, config.sz); close(fd); diff --git a/native/jni/init/rootdir.cpp b/native/jni/init/rootdir.cpp index a7c5172d1..1c3f76836 100644 --- a/native/jni/init/rootdir.cpp +++ b/native/jni/init/rootdir.cpp @@ -81,33 +81,6 @@ static void load_overlay_rc(const char *overlay) { } } -void RootFSInit::setup_rootfs() { - if (patch_sepolicy("/sepolicy")) { - auto init = raw_data::mmap_rw("/init"); - init.patch({ make_pair(SPLIT_PLAT_CIL, "xxx") }); - } - - // Handle overlays - if (access("/overlay.d", F_OK) == 0) { - LOGD("Merge overlay.d\n"); - load_overlay_rc("/overlay.d"); - mv_path("/overlay.d", "/"); - } - - patch_init_rc("/init.rc", "/init.p.rc", "/sbin"); - rename("/init.p.rc", "/init.rc"); - - // Create hardlink mirror of /sbin to /root - mkdir("/root", 0750); - clone_attr("/sbin", "/root"); - link_path("/sbin", "/root"); - - // Dump magiskinit as magisk - int fd = xopen("/sbin/magisk", O_WRONLY | O_CREAT, 0755); - write(fd, self.buf, self.sz); - close(fd); -} - bool MagiskInit::patch_sepolicy(const char *file) { bool patch_init = false; sepolicy *sepol = nullptr; @@ -135,12 +108,14 @@ bool MagiskInit::patch_sepolicy(const char *file) { sepol->magisk_rules(); // Custom rules - if (auto dir = open_dir(persist_dir.data()); dir) { - for (dirent *entry; (entry = xreaddir(dir.get()));) { - auto rule = persist_dir + "/" + entry->d_name + "/sepolicy.rule"; - if (access(rule.data(), R_OK) == 0) { - LOGD("Loading custom sepolicy patch: %s\n", rule.data()); - sepol->load_rule_file(rule.data()); + if (!custom_rules_dir.empty()) { + if (auto dir = open_dir(custom_rules_dir.data())) { + for (dirent *entry; (entry = xreaddir(dir.get()));) { + auto rule = custom_rules_dir + "/" + entry->d_name + "/sepolicy.rule"; + if (access(rule.data(), R_OK) == 0) { + LOGD("Loading custom sepolicy patch: %s\n", rule.data()); + sepol->load_rule_file(rule.data()); + } } } } @@ -229,9 +204,8 @@ void SARBase::patch_rootdir() { } setup_tmp(tmp_dir); - persist_dir = MIRRDIR "/persist/magisk"; - chdir(tmp_dir); + mount_rules_dir(BLOCKDIR, MIRRDIR); // Mount system_root mirror struct stat st; @@ -317,11 +291,55 @@ void SARBase::patch_rootdir() { chdir("/"); } +#define TMP_MNTDIR "/dev/mnt" +#define TMP_RULESDIR "/.backup/.sepolicy.rules" + +void RootFSInit::setup_rootfs() { + // Handle custom sepolicy rules + xmkdir(TMP_MNTDIR, 0755); + mount_rules_dir("/dev/block", TMP_MNTDIR); + // Preserve custom rule path + if (!custom_rules_dir.empty()) { + string rules_dir = "./" + custom_rules_dir.substr(sizeof(TMP_MNTDIR)); + xsymlink(rules_dir.data(), TMP_RULESDIR); + } + + if (patch_sepolicy("/sepolicy")) { + auto init = raw_data::mmap_rw("/init"); + init.patch({ make_pair(SPLIT_PLAT_CIL, "xxx") }); + } + + // Handle overlays + if (access("/overlay.d", F_OK) == 0) { + LOGD("Merge overlay.d\n"); + load_overlay_rc("/overlay.d"); + mv_path("/overlay.d", "/"); + } + + patch_init_rc("/init.rc", "/init.p.rc", "/sbin"); + rename("/init.p.rc", "/init.rc"); + + // Create hardlink mirror of /sbin to /root + mkdir("/root", 0750); + clone_attr("/sbin", "/root"); + link_path("/sbin", "/root"); + + // Dump magiskinit as magisk + int fd = xopen("/sbin/magisk", O_WRONLY | O_CREAT, 0755); + write(fd, self.buf, self.sz); + close(fd); +} + void MagiskProxy::start() { + // Mount rootfs as rw to do post-init rootfs patches + xmount(nullptr, "/", nullptr, MS_REMOUNT, nullptr); + + // Backup stuffs before removing them self = raw_data::read("/sbin/magisk"); config = raw_data::read("/.backup/.magisk"); - - xmount(nullptr, "/", nullptr, MS_REMOUNT, nullptr); + char custom_rules_dir[64]; + custom_rules_dir[0] = '\0'; + xreadlink(TMP_RULESDIR, custom_rules_dir, sizeof(custom_rules_dir)); unlink("/sbin/magisk"); rm_rf("/.backup"); @@ -331,6 +349,10 @@ void MagiskProxy::start() { // Create symlinks pointing back to /root recreate_sbin("/root", false); + if (custom_rules_dir[0]) + xsymlink(custom_rules_dir, "/sbin/" RULESDIR); + + // Tell magiskd to remount rootfs setenv("REMOUNT_ROOT", "1", 1); execv("/sbin/magisk", argv); } diff --git a/scripts/boot_patch.sh b/scripts/boot_patch.sh index f0189691d..3bdba05bb 100644 --- a/scripts/boot_patch.sh +++ b/scripts/boot_patch.sh @@ -178,5 +178,8 @@ ui_print "- Repacking boot image" # Sign chromeos boot $CHROMEOS && sign_chromeos +# Copy existing rules for migration +$BOOTMODE && copy_sepolicy_rules + # Reset any error code true diff --git a/scripts/magisk_uninstaller.sh b/scripts/magisk_uninstaller.sh index 03f176128..0c3683126 100644 --- a/scripts/magisk_uninstaller.sh +++ b/scripts/magisk_uninstaller.sh @@ -15,7 +15,6 @@ TMPDIR=/dev/tmp INSTALLER=$TMPDIR/install CHROMEDIR=$INSTALLER/chromeos -PERSISTDIR=/sbin/.magisk/mirror/persist # Default permissions umask 022 @@ -36,7 +35,12 @@ setup_flashable print_title "Magisk Uninstaller" is_mounted /data || mount /data || abort "! Unable to mount /data, please uninstall with Magisk Manager" -is_mounted /cache || mount /cache 2>/dev/null +if ! $BOOTMODE; then + # Mounting stuffs in recovery (best effort) + mount_name metadata /metadata + mount_name "cache cac" /cache + mount_name persist /persist +fi mount_partitions api_level_arch_detect @@ -141,7 +145,8 @@ ui_print "- Removing Magisk files" rm -rf \ /cache/*magisk* /cache/unblock /data/*magisk* /data/cache/*magisk* /data/property/*magisk* \ /data/Magisk.apk /data/busybox /data/custom_ramdisk_patch.sh /data/adb/*magisk* \ -/data/adb/post-fs-data.d /data/adb/service.d /data/adb/modules* $PERSISTDIR/magisk 2>/dev/null +/data/adb/post-fs-data.d /data/adb/service.d /data/adb/modules* \ +/data/unencrypted/magisk /metadata/magisk /persist/magisk /mnt/vendor/persist/magisk if [ -f /system/addon.d/99-magisk.sh ]; then blockdev --setrw /dev/block/mapper/system$SLOT 2>/dev/null @@ -158,7 +163,7 @@ if $BOOTMODE; then ui_print "********************************************" (sleep 8; /system/bin/reboot)& else - rm -rf /data/user*/*/*magisk* /data/app/*magisk* + rm -rf /data/data/*magisk* /data/user*/*/*magisk* /data/app/*magisk* /data/app/*/*magisk* recovery_cleanup ui_print "- Done" fi diff --git a/scripts/util_functions.sh b/scripts/util_functions.sh index 2491f3215..00e3cbd9b 100644 --- a/scripts/util_functions.sh +++ b/scripts/util_functions.sh @@ -152,6 +152,7 @@ recovery_cleanup() { fi umount -l /vendor umount -l /persist + umount -l /metadata for DIR in /apex /system /system_root; do if [ -L "${DIR}_link" ]; then rmdir $DIR @@ -217,13 +218,13 @@ mount_name() { local FLAG=$3 setup_mntpoint $POINT 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 + local BLOCK=$(find_block $PART) + mount $FLAG $BLOCK $POINT || return fi + ui_print "- Mounting $POINT" } # mount_ro_ensure @@ -266,18 +267,6 @@ mount_partitions() { # Allow /system/bin commands (dalvikvm) on Android 10+ in recovery $BOOTMODE || mount_apex - - # 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 cac" /cache - is_mounted /cache && PERSISTDIR=/cache || PERSISTDIR= - fi - fi } # loop_setup , sets LOOPDEV @@ -575,6 +564,41 @@ run_migrations() { done } +copy_sepolicy_rules() { + # Remove all existing rule folders + rm -rf /data/unencrypted/magisk /metadata/magisk /persist/magisk /mnt/vendor/persist/magisk + + # Find current active RULESDIR + local RULESDIR + local active_dir=$(magisk --path)/.magisk/mirror/sepolicy.rules + if [ -e $active_dir ]; then + RULESDIR=$(readlink -f $active_dir) + elif [ -d /data/unencrypted ] && ! grep ' /data ' /proc/mounts | grep -q 'dm-'; then + RULESDIR=/data/unencrypted/magisk + elif grep -q ' /cache ' /proc/mounts; then + RULESDIR=/cache/magisk + elif grep -q ' /metadata ' /proc/mounts; then + RULESDIR=/metadata/magisk + elif grep -q ' /persist ' /proc/mounts; then + RULESDIR=/persist/magisk + elif grep -q ' /mnt/vendor/persist ' /proc/mounts; then + RULESDIR=/mnt/vendor/persist/magisk + else + return + fi + + # Copy all enabled sepolicy.rule + for r in /data/adb/modules*/*/sepolicy.rule; do + [ -f "$r" ] || continue + local MODDIR=${r%/*} + [ -f $MODDIR/disable ] && continue + [ -f $MODDIR/remove ] && continue + local MODNAME=${MODDIR##*/} + mkdir -p $RULESDIR/$MODNAME + cp -f $r $RULESDIR/$MODNAME/sepolicy.rule + done +} + ################# # Module Related ################# @@ -620,9 +644,6 @@ is_legacy_script() { # Require OUTFD, ZIPFILE to be set install_module() { - local PERSISTDIR - command -v magisk >/dev/null && PERSISTDIR=$(magisk --path)/mirror/persist - rm -rf $TMPDIR mkdir -p $TMPDIR @@ -646,7 +667,7 @@ install_module() { MODPATH=$MODULEROOT/$MODID # Create mod paths - rm -rf $MODPATH 2>/dev/null + rm -rf $MODPATH mkdir -p $MODPATH if is_legacy_script; then @@ -699,19 +720,15 @@ install_module() { fi # Copy over custom sepolicy rules - if [ -f $MODPATH/sepolicy.rule -a -e "$PERSISTDIR" ]; then - ui_print "- Installing custom sepolicy patch" - # Remove old recovery logs (which may be filling partition) to make room - rm -f $PERSISTDIR/cache/recovery/* - PERSISTMOD=$PERSISTDIR/magisk/$MODID - mkdir -p $PERSISTMOD - cp -af $MODPATH/sepolicy.rule $PERSISTMOD/sepolicy.rule || abort "! Insufficient partition size" + if [ -f $MODPATH/sepolicy.rule ]; then + ui_print "- Installing custom sepolicy rules" + copy_sepolicy_rules fi # Remove stuffs that don't belong to modules rm -rf \ $MODPATH/system/placeholder $MODPATH/customize.sh \ - $MODPATH/README.md $MODPATH/.git* 2>/dev/null + $MODPATH/README.md $MODPATH/.git* cd / $BOOTMODE || recovery_cleanup