diff --git a/native/src/base/files.cpp b/native/src/base/files.cpp index 4012d2e6f..d0942a586 100644 --- a/native/src/base/files.cpp +++ b/native/src/base/files.cpp @@ -505,3 +505,15 @@ string find_apk_path(const char *pkg) { string path(buf); return path.append("/base.apk"); } + +string find_rules_dir(const char *base_dir) { + string rules_dir = base_dir; + if (access((rules_dir + "/unencrypted").data(), F_OK) == 0) { + rules_dir += "/unencrypted/magisk"; + } else if (access((rules_dir + "/adb").data(), F_OK) == 0) { + rules_dir += "/adb/modules"; + } else { + rules_dir += "/magisk"; + } + return rules_dir; +} diff --git a/native/src/base/files.hpp b/native/src/base/files.hpp index ea27cfd75..d7d259457 100644 --- a/native/src/base/files.hpp +++ b/native/src/base/files.hpp @@ -107,6 +107,7 @@ void frm_rf(int dirfd); void clone_dir(int src, int dest); std::vector parse_mount_info(const char *pid); std::string find_apk_path(const char *pkg); +std::string find_rules_dir(const char *base_dir); using sFILE = std::unique_ptr; using sDIR = std::unique_ptr; diff --git a/native/src/core/bootstages.cpp b/native/src/core/bootstages.cpp index d80f0c460..9b9a6f732 100644 --- a/native/src/core/bootstages.cpp +++ b/native/src/core/bootstages.cpp @@ -45,6 +45,7 @@ static bool mount_mirror(const std::string_view from, const std::string_view to) static void mount_mirrors() { LOGI("* Mounting mirrors\n"); + auto self_mount_info = parse_mount_info("self"); // Bind remount module root to clear nosuid if (access(SECURE_DIR, F_OK) == 0 || SDK_INT < 24) { @@ -59,6 +60,34 @@ static void mount_mirrors() { restorecon(); } + // check and mount sepolicy.rules + { + dev_t rules_dev; + auto rules = MAGISKTMP + "/" BLOCKDIR "/rules"; + if (struct stat st{}; stat(rules.data(), &st) == 0 && (st.st_mode & S_IFBLK)) { + rules_dev = st.st_rdev; + } else { + // install from recovery, find now + // this helps Magisk app to copy sepolicy.rules when fixing environment + rules_dev = find_rules_device(self_mount_info); + } + + for (const auto &info: self_mount_info) { + if (info.root == "/" && info.device == rules_dev) { + auto flags = split_ro(info.fs_option, ","); + auto rw = std::any_of(flags.begin(), flags.end(), [](const auto &flag) { + return flag == "rw"sv; + }); + if (!rw) continue; + string custom_rules_dir = find_rules_dir(info.target.data()); + xmkdir(custom_rules_dir.data(), 0700); + auto rules_dir = MAGISKTMP + "/" RULESDIR; + mount_mirror(custom_rules_dir, rules_dir); + break; + } + } + } + // Prepare worker auto worker_dir = MAGISKTMP + "/" WORKERDIR; xmount("worker", worker_dir.data(), "tmpfs", 0, "mode=755"); @@ -69,7 +98,7 @@ static void mount_mirrors() { LOGI("fallback to mount subtree\n"); // rootfs may fail, fallback to bind mount each mount point set> mounted_dirs {{ MAGISKTMP }}; - for (const auto &info: parse_mount_info("self")) { + for (const auto &info: self_mount_info) { if (info.type == "rootfs"sv) continue; // the greatest mount point that less than info.target, which is possibly a parent if (auto last_mount = mounted_dirs.upper_bound(info.target); @@ -84,6 +113,50 @@ static void mount_mirrors() { } } +dev_t find_rules_device(const std::vector &infos) { + const int UNKNOWN = 0; + const int PERSIST = 1; + const int METADATA = 2; + const int CACHE = 3; + const int DATA = 4; + int matched = UNKNOWN; + dev_t rules_dev = 0; + bool encrypted = getprop("ro.crypto.state") == "encrypted"; + + for (const auto &info: infos) { + if (info.target.ends_with(RULESDIR)) + return info.device; + if (info.root != "/" || info.source.find("/dm-") != string::npos) + continue; + if (info.type != "ext4" && info.type != "f2fs") + continue; + auto flags = split_ro(info.fs_option, ","); + auto rw = std::any_of(flags.begin(), flags.end(), [](const auto &flag) { + return flag == "rw"sv; + }); + if (!rw) continue; + int new_matched; + if (info.target == "/cache" && matched < CACHE) { + new_matched = CACHE; + } else if (info.target == "/data" && matched < DATA) { + if (encrypted && access("/data/unencrypted", F_OK)) { + continue; + } else { + new_matched = DATA; + } + } else if (info.target == "/metadata" && matched < METADATA) { + new_matched = METADATA; + } else if ((info.target == "/persist" || info.target == "/mnt/vendor/persist") && + matched < PERSIST) { + new_matched = PERSIST; + } else continue; + + rules_dev = info.device; + matched = new_matched; + } + return rules_dev; +} + static bool magisk_env() { char buf[4096]; diff --git a/native/src/core/core.hpp b/native/src/core/core.hpp index a02d618de..492bdb673 100644 --- a/native/src/core/core.hpp +++ b/native/src/core/core.hpp @@ -6,6 +6,7 @@ extern bool RECOVERY_MODE; extern std::atomic pkg_xml_ino; +dev_t find_rules_device(const std::vector &infos); void unlock_blocks(); void reboot(); void start_log_daemon(); diff --git a/native/src/core/magisk.cpp b/native/src/core/magisk.cpp index 9405cbd47..7d5832a87 100644 --- a/native/src/core/magisk.cpp +++ b/native/src/core/magisk.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -133,6 +134,10 @@ int magisk_main(int argc, char *argv[]) { return 0; } else if (argc >= 3 && argv[1] == "--install-module"sv) { install_module(argv[2]); + } else if (argv[1] == "--rules-device"sv) { + auto dev = find_rules_device(parse_mount_info("self")); + if (dev) printf("%u:%u\n", major(dev), minor(dev)); + return dev ? 0 : 1; } #if 0 /* Entry point for testing stuffs */ diff --git a/native/src/init/init.cpp b/native/src/init/init.cpp index f336dcd92..8417bd67e 100644 --- a/native/src/init/init.cpp +++ b/native/src/init/init.cpp @@ -83,9 +83,6 @@ int main(int argc, char *argv[]) { if (name == "magisk"sv) return magisk_proxy_main(argc, argv); - if (name == "magiskinit"sv && argc == 2 && argv[1] == "--rules-device"sv) - return rust::print_rules_device(); - if (getpid() != 1) return 1; diff --git a/native/src/init/init.hpp b/native/src/init/init.hpp index e75709f1b..aaa92a3be 100644 --- a/native/src/init/init.hpp +++ b/native/src/init/init.hpp @@ -57,7 +57,12 @@ public: class MagiskInit : public BaseInit { private: - void mount_rules_dir(); + dev_t rules_dev = 0; + + void parse_config_file(); + void patch_sepolicy(const char *in, const char *out); + bool hijack_sepolicy(); + void setup_tmp(const char *path); protected: #if ENABLE_AVD_HACK @@ -66,9 +71,6 @@ protected: bool avd_hack = false; #endif - void patch_sepolicy(const char *in, const char *out); - bool hijack_sepolicy(); - void setup_tmp(const char *path); void patch_rw_root(); void patch_ro_root(); public: diff --git a/native/src/init/lib.rs b/native/src/init/lib.rs index b53c31f19..b46572960 100644 --- a/native/src/init/lib.rs +++ b/native/src/init/lib.rs @@ -1,9 +1,3 @@ -use std::env; -use std::path::Path; - -pub use base; -use base::libc::{dev_t, makedev}; -use base::read_lines; pub use logging::*; mod logging; @@ -12,74 +6,5 @@ mod logging; pub mod ffi2 { extern "Rust" { fn setup_klog(); - fn print_rules_device() -> i32; } } - -pub fn print_rules_device() -> i32 { - const UNKNOWN: i32 = 0; - const PERSIST: i32 = UNKNOWN + 1; - const METADATA: i32 = PERSIST + 1; - const CACHE: i32 = METADATA + 1; - const UNENCRYPTED: i32 = CACHE + 1; - const DATA: i32 = UNENCRYPTED + 1; - const EXISTING: i32 = DATA + 1; - - let encrypted = env::var("ISENCRYPTED").map_or(false, |var| var == "true"); - - let mut matched = UNKNOWN; - let mut rules_dev: dev_t = 0; - - if let Ok(lines) = read_lines("/proc/self/mountinfo") { - for line in lines { - if let Ok(line) = line { - let new_matched; - if line.contains("/.magisk/sepolicy.rules ") { - new_matched = EXISTING; - } else if line.contains(" - ext4 ") && !line.contains("/dm-") { - if line.contains(" / /cache ") && matched < CACHE { - new_matched = CACHE; - } else if line.contains(" / /data ") && matched < DATA { - if !encrypted { - new_matched = UNENCRYPTED; - } else if Path::new("/data/unencrypted").is_dir() { - new_matched = DATA; - } else { - continue; - } - } else if line.contains(" / /metadata ") && matched < METADATA { - new_matched = METADATA; - } else if (line.contains(" / /persist ") - || line.contains(" / /mnt/vendor/persist ")) - && matched < PERSIST - { - new_matched = PERSIST; - } else { - continue; - } - } else { - continue; - } - if let Some(device) = line.splitn(4, ' ').nth(2) { - device.split_once(':').map(|(a, b)| { - a.parse::().ok().map(|a| { - b.parse::().ok().map(|b| { - rules_dev = unsafe { makedev(a, b) }; - matched = new_matched; - }) - }) - }); - } - } - } - if matched > UNKNOWN { - println!("{rules_dev}"); - return 0; - } else { - eprintln!("Failed to find sepolicy rules partition"); - } - } else { - eprintln!("Error reading /proc/self/mountinfo"); - } - return 1; -} diff --git a/native/src/init/mount.cpp b/native/src/init/mount.cpp index 42095e29f..ec1dcb25f 100644 --- a/native/src/init/mount.cpp +++ b/native/src/init/mount.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include @@ -112,33 +111,38 @@ static void switch_root(const string &path) { frm_rf(root); } -void MagiskInit::mount_rules_dir() { - dev_t rules_dev = 0; - parse_prop_file(".backup/.magisk", [&rules_dev](auto key, auto value) -> bool { - if (key == "RULESDEVICE") { - sscanf(value.data(), "%" PRIuPTR, &rules_dev); - return false; - } - return true; - }); +static void mount_rules_dir(string path, dev_t rules_dev) { if (!rules_dev) return; xmknod(BLOCKDIR "/rules", S_IFBLK | 0600, rules_dev); xmkdir(MIRRDIR "/rules", 0); - if (xmount(BLOCKDIR "/rules", MIRRDIR "/rules", "ext4", 0, nullptr) == 0) { - string custom_rules_dir = MIRRDIR "/rules"; - if (access((custom_rules_dir + "/unencrypted").data(), F_OK) == 0) { - custom_rules_dir += "/unencrypted/magisk"; - } else if (access((custom_rules_dir + "/adb").data(), F_OK) == 0) { - custom_rules_dir += "/adb/modules"; - } else { - custom_rules_dir += "/magisk"; + + bool mounted = false; + // first of all, find if rules dev is already mounted + for (auto &info : parse_mount_info("self")) { + if (info.root == "/" && info.device == rules_dev) { + // Already mounted, just bind mount + xmount(info.target.data(), MIRRDIR "/rules", nullptr, MS_BIND, nullptr); + mounted = true; + break; } + } + + if (mounted || mount(BLOCKDIR "/rules", MIRRDIR "/rules", "ext4", MS_RDONLY, nullptr) == 0 || + mount(BLOCKDIR "/rules", MIRRDIR "/rules", "f2fs", MS_RDONLY, nullptr) == 0) { + string custom_rules_dir = find_rules_dir(MIRRDIR "/rules"); // Create bind mount xmkdirs(RULESDIR, 0); - xmkdirs(custom_rules_dir.data(), 0700); - LOGD("sepolicy.rules: %s -> %s\n", custom_rules_dir.data(), RULESDIR); - xmount(custom_rules_dir.data(), RULESDIR, nullptr, MS_BIND, nullptr); + if (access(custom_rules_dir.data(), F_OK)) { + LOGW("empty sepolicy.rules: %s\n", custom_rules_dir.data()); + } else { + LOGD("sepolicy.rules: %s\n", custom_rules_dir.data()); + xmount(custom_rules_dir.data(), RULESDIR, nullptr, MS_BIND, nullptr); + mount_list.emplace_back(path += "/" RULESDIR); + } xumount2(MIRRDIR "/rules", MNT_DETACH); + } else { + PLOGE("Failed to mount sepolicy.rules %u:%u", major(rules_dev), minor(rules_dev)); + unlink(BLOCKDIR "/rules"); } } @@ -242,7 +246,7 @@ void MagiskInit::setup_tmp(const char *path) { xmkdir(BLOCKDIR, 0); xmkdir(WORKERDIR, 0); - mount_rules_dir(); + mount_rules_dir(path, rules_dev); cp_afc(".backup/.magisk", INTLROOT "/config"); rm_rf(".backup"); diff --git a/native/src/init/rootdir.cpp b/native/src/init/rootdir.cpp index 4e521c988..4bb0c36e5 100644 --- a/native/src/init/rootdir.cpp +++ b/native/src/init/rootdir.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -181,11 +182,27 @@ static void extract_files(bool sbin) { } } +void MagiskInit::parse_config_file() { + dev_t dev = 0; + parse_prop_file("/data/.backup/.magisk", [&dev](auto key, auto value) -> bool { + if (key == "RULESDEVICE") { + unsigned int dev_major = 0; + unsigned int dev_minor = 0; + sscanf(value.data(), "%u:%u", &dev_major, &dev_minor); + dev = makedev(dev_major, dev_minor); + return false; + } + return true; + }); + rules_dev = dev; +} + #define ROOTMIR MIRRDIR "/system_root" #define NEW_INITRC "/system/etc/init/hw/init.rc" void MagiskInit::patch_ro_root() { mount_list.emplace_back("/data"); + parse_config_file(); string tmp_dir; @@ -272,6 +289,8 @@ void RootFSInit::prepare() { void MagiskInit::patch_rw_root() { mount_list.emplace_back("/data"); + parse_config_file(); + // Create hardlink mirror of /sbin to /root mkdir("/root", 0777); clone_attr("/sbin", "/root"); diff --git a/scripts/avd_patch.sh b/scripts/avd_patch.sh index 0774998b2..4b1215260 100644 --- a/scripts/avd_patch.sh +++ b/scripts/avd_patch.sh @@ -57,10 +57,16 @@ done ./magiskboot decompress ramdisk.cpio.tmp ramdisk.cpio cp ramdisk.cpio ramdisk.cpio.orig -touch config - -echo "RULESDEVICE=$(ISENCRYPTED=true ./magiskinit --rules-device)" >> config +export KEEPVERITY=false +export KEEPFORCEENCRYPT=true +echo "KEEPVERITY=$KEEPVERITY" > config +echo "KEEPFORCEENCRYPT=$KEEPFORCEENCRYPT" >> config +if [ -e "/system/bin/linker64" ]; then + echo "RULESDEVICE=$(./magisk64 --rules-device)" >> config +else + echo "RULESDEVICE=$(./magisk32 --rules-device)" >> config +fi # For API 28, we also patch advancedFeatures.ini to disable SAR # Manually override skip_initramfs by setting RECOVERYMODE=true [ $API = "28" ] && echo 'RECOVERYMODE=true' >> config @@ -69,9 +75,6 @@ echo "RULESDEVICE=$(ISENCRYPTED=true ./magiskinit --rules-device)" >> config ./magiskboot compress=xz magisk64 magisk64.xz ./magiskboot compress=xz stub.apk stub.xz -export KEEPVERITY=false -export KEEPFORCEENCRYPT=true - ./magiskboot cpio ramdisk.cpio \ "add 0750 init magiskinit" \ "mkdir 0750 overlay.d" \ diff --git a/scripts/boot_patch.sh b/scripts/boot_patch.sh index b4184df41..a63da21f5 100644 --- a/scripts/boot_patch.sh +++ b/scripts/boot_patch.sh @@ -74,16 +74,12 @@ fi [ -z $PATCHVBMETAFLAG ] && PATCHVBMETAFLAG=false [ -z $RECOVERYMODE ] && RECOVERYMODE=false [ -z $SYSTEM_ROOT ] && SYSTEM_ROOT=false -[ -z $ISENCRYPTED ] && ISENCRYPTED=false export KEEPVERITY export KEEPFORCEENCRYPT export PATCHVBMETAFLAG -export ISENCRYPTED chmod -R 755 . -RULESDEVICE="$(./magiskinit --rules-device)" || abort "! Unable to find rules partition!" - ######### # Unpack ######### @@ -153,26 +149,29 @@ fi ui_print "- Patching ramdisk" -echo "KEEPVERITY=$KEEPVERITY" > config -echo "KEEPFORCEENCRYPT=$KEEPFORCEENCRYPT" >> config -echo "PATCHVBMETAFLAG=$PATCHVBMETAFLAG" >> config -echo "RECOVERYMODE=$RECOVERYMODE" >> config -echo "RULESDEVICE=$RULESDEVICE" >> config -[ ! -z $SHA1 ] && echo "SHA1=$SHA1" >> config - # Compress to save precious ramdisk space SKIP32="#" SKIP64="#" if [ -f magisk32 ]; then + $BOOTMODE && [ -z "$RULESDEVICE" ] && RULESDEVICE=$(./magisk32 --rules-device) ./magiskboot compress=xz magisk32 magisk32.xz unset SKIP32 fi if [ -f magisk64 ]; then + $BOOTMODE && [ -z "$RULESDEVICE" ] && RULESDEVICE=$(./magisk64 --rules-device) ./magiskboot compress=xz magisk64 magisk64.xz unset SKIP64 fi ./magiskboot compress=xz stub.apk stub.xz +echo "KEEPVERITY=$KEEPVERITY" > config +echo "KEEPFORCEENCRYPT=$KEEPFORCEENCRYPT" >> config +echo "PATCHVBMETAFLAG=$PATCHVBMETAFLAG" >> config +echo "RECOVERYMODE=$RECOVERYMODE" >> config +[ -n "$RULESDEVICE" ] && ui_print "- Rules partition device ID: $RULESDEVICE" +[ -n "$RULESDEVICE" ] && echo "RULESDEVICE=$RULESDEVICE" >> config +[ -n "$SHA1" ] && echo "SHA1=$SHA1" >> config + ./magiskboot cpio ramdisk.cpio \ "add 0750 $INIT magiskinit" \ "mkdir 0750 overlay.d" \