mirror of
https://github.com/topjohnwu/Magisk.git
synced 2026-07-03 14:08:39 +02:00
272 lines
8.5 KiB
C++
272 lines
8.5 KiB
C++
#include <set>
|
|
#include <sys/mount.h>
|
|
#include <sys/sysmacros.h>
|
|
#include <libgen.h>
|
|
|
|
#include <base.hpp>
|
|
#include <flags.h>
|
|
#include <consts.hpp>
|
|
|
|
#include "init.hpp"
|
|
|
|
using namespace std;
|
|
|
|
struct devinfo {
|
|
int major;
|
|
int minor;
|
|
char devname[32];
|
|
char partname[32];
|
|
char dmname[32];
|
|
char devpath[PATH_MAX];
|
|
};
|
|
|
|
static vector<devinfo> dev_list;
|
|
|
|
// When this boolean is set, this means we are currently
|
|
// running magiskinit on legacy SAR AVD emulator
|
|
bool avd_hack = false;
|
|
|
|
static void parse_device(devinfo *dev, const char *uevent) {
|
|
dev->partname[0] = '\0';
|
|
dev->devpath[0] = '\0';
|
|
dev->dmname[0] = '\0';
|
|
dev->devname[0] = '\0';
|
|
parse_prop_file(uevent, [=](string_view key, string_view value) -> bool {
|
|
if (key == "MAJOR")
|
|
dev->major = parse_int(value.data());
|
|
else if (key == "MINOR")
|
|
dev->minor = parse_int(value.data());
|
|
else if (key == "DEVNAME")
|
|
strscpy(dev->devname, value.data(), sizeof(dev->devname));
|
|
else if (key == "PARTNAME")
|
|
strscpy(dev->partname, value.data(), sizeof(dev->devname));
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
void MagiskInit::collect_devices() const noexcept {
|
|
char path[PATH_MAX];
|
|
devinfo dev{};
|
|
if (auto dir = xopen_dir("/sys/dev/block"); dir) {
|
|
for (dirent *entry; (entry = readdir(dir.get()));) {
|
|
if (entry->d_name == "."sv || entry->d_name == ".."sv)
|
|
continue;
|
|
sprintf(path, "/sys/dev/block/%s/uevent", entry->d_name);
|
|
parse_device(&dev, path);
|
|
sprintf(path, "/sys/dev/block/%s/dm/name", entry->d_name);
|
|
if (access(path, F_OK) == 0) {
|
|
auto name = rtrim(full_read(path));
|
|
strscpy(dev.dmname, name.data(), sizeof(dev.dmname));
|
|
}
|
|
if (auto it = std::ranges::find_if(config.partition_map, [&](const auto &i) {
|
|
return i.key == dev.devname;
|
|
}); dev.partname[0] == '\0' && it != config.partition_map.end()) {
|
|
// use androidboot.partition_map as partname fallback.
|
|
strscpy(dev.partname, it->value.data(), sizeof(dev.partname));
|
|
}
|
|
sprintf(path, "/sys/dev/block/%s", entry->d_name);
|
|
xrealpath(path, dev.devpath, sizeof(dev.devpath));
|
|
dev_list.push_back(dev);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint64_t MagiskInit::find_block(const char *partname) const noexcept {
|
|
if (dev_list.empty())
|
|
collect_devices();
|
|
|
|
for (int tries = 0; tries < 3; ++tries) {
|
|
for (auto &dev : dev_list) {
|
|
const char *name;
|
|
if (strcasecmp(dev.partname, partname) == 0)
|
|
name = dev.partname;
|
|
else if (strcasecmp(dev.dmname, partname) == 0)
|
|
name = dev.dmname;
|
|
else if (strcasecmp(dev.devname, partname) == 0)
|
|
name = dev.devname;
|
|
else if (std::string_view(dev.devpath).ends_with("/"s + partname))
|
|
name = dev.devpath;
|
|
else
|
|
continue;
|
|
|
|
LOGD("Found %s: [%s] (%d, %d)\n", name, dev.devname, dev.major, dev.minor);
|
|
return makedev(dev.major, dev.minor);
|
|
}
|
|
// Wait 10ms and try again
|
|
usleep(10000);
|
|
dev_list.clear();
|
|
collect_devices();
|
|
}
|
|
|
|
// The requested partname does not exist
|
|
return 0;
|
|
}
|
|
|
|
void MagiskInit::mount_preinit_dir() const noexcept {
|
|
if (preinit_dev.empty()) return;
|
|
auto dev = find_block(preinit_dev.data());
|
|
if (dev == 0) {
|
|
LOGE("Cannot find preinit %s, abort!\n", preinit_dev.data());
|
|
return;
|
|
}
|
|
xmknod(PREINITDEV, S_IFBLK | 0600, dev);
|
|
xmkdir(MIRRDIR, 0);
|
|
bool mounted = false;
|
|
// First, find if it is already mounted
|
|
std::string mnt_point;
|
|
if (rust::is_device_mounted(dev, mnt_point)) {
|
|
// Already mounted, just bind mount
|
|
xmount(mnt_point.data(), MIRRDIR, nullptr, MS_BIND, nullptr);
|
|
mounted = true;
|
|
}
|
|
|
|
// Since we are mounting the block device directly, make sure to ONLY mount the partitions
|
|
// as read-only, or else the kernel might crash due to crappy drivers.
|
|
// After the device boots up, magiskd will properly bind mount the correct partition
|
|
// on to PREINITMIRR as writable. For more details, check bootstages.cpp
|
|
if (mounted || mount(PREINITDEV, MIRRDIR, "ext4", MS_RDONLY, nullptr) == 0 ||
|
|
mount(PREINITDEV, MIRRDIR, "f2fs", MS_RDONLY, nullptr) == 0) {
|
|
string preinit_dir = resolve_preinit_dir(MIRRDIR);
|
|
// Create bind mount
|
|
xmkdirs(PREINITMIRR, 0);
|
|
if (access(preinit_dir.data(), F_OK)) {
|
|
LOGW("empty preinit: %s\n", preinit_dir.data());
|
|
} else {
|
|
LOGD("preinit: %s\n", preinit_dir.data());
|
|
xmount(preinit_dir.data(), PREINITMIRR, nullptr, MS_BIND, nullptr);
|
|
}
|
|
xumount2(MIRRDIR, MNT_DETACH);
|
|
} else {
|
|
PLOGE("Failed to mount preinit %s\n", preinit_dev.data());
|
|
unlink(PREINITDEV);
|
|
}
|
|
}
|
|
|
|
bool MagiskInit::mount_system_root() noexcept {
|
|
LOGD("Mounting system_root\n");
|
|
|
|
// there's no /dev in stub cpio
|
|
xmkdir("/dev", 0777);
|
|
|
|
dev_t dev;
|
|
do {
|
|
// Try legacy SAR dm-verity
|
|
dev = find_block("vroot");
|
|
if (dev > 0)
|
|
goto mount_root;
|
|
|
|
// Try NVIDIA naming scheme
|
|
dev = find_block("APP");
|
|
if (dev > 0)
|
|
goto mount_root;
|
|
|
|
// Try normal partname
|
|
char sys_part[32];
|
|
sprintf(sys_part, "system%s", config.slot.data());
|
|
dev = find_block(sys_part);
|
|
if (dev > 0)
|
|
goto mount_root;
|
|
|
|
// Poll forever if rootwait was given in cmdline
|
|
} while (config.rootwait);
|
|
|
|
// We don't really know what to do at this point...
|
|
LOGE("Cannot find root partition, abort\n");
|
|
exit(1);
|
|
|
|
mount_root:
|
|
xmknod("/dev/root", S_IFBLK | 0600, dev);
|
|
xmkdir("/system_root", 0755);
|
|
|
|
if (xmount("/dev/root", "/system_root", "ext4", MS_RDONLY, nullptr)) {
|
|
if (xmount("/dev/root", "/system_root", "erofs", MS_RDONLY, nullptr)) {
|
|
// We don't really know what to do at this point...
|
|
LOGE("Cannot mount root partition, abort\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
rust::switch_root("/system_root");
|
|
|
|
// Make dev writable
|
|
xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755");
|
|
mount_list.emplace_back("/dev");
|
|
|
|
bool is_two_stage = access("/system/bin/init", F_OK) == 0;
|
|
LOGD("is_two_stage: [%d]\n", is_two_stage);
|
|
|
|
// For API 28 AVD, it uses legacy SAR setup that requires
|
|
// special hacks in magiskinit to work properly.
|
|
if (!is_two_stage && config.emulator) {
|
|
avd_hack = true;
|
|
// These values are hardcoded for API 28 AVD
|
|
auto vendor_dev = find_block("vendor");
|
|
xmkdir("/dev/block", 0755);
|
|
xmknod("/dev/block/vde1", S_IFBLK | 0600, vendor_dev);
|
|
xmount("/dev/block/vde1", "/vendor", "ext4", MS_RDONLY, nullptr);
|
|
}
|
|
|
|
return is_two_stage;
|
|
}
|
|
|
|
void MagiskInit::exec_init() const noexcept {
|
|
// Unmount in reverse order
|
|
for (auto i = mount_list.size(); i > 0; --i) {
|
|
auto &p = mount_list[i - 1];
|
|
if (xumount2(p.data(), MNT_DETACH) == 0)
|
|
LOGD("Unmount [%s]\n", p.data());
|
|
}
|
|
execve("/init", argv, environ);
|
|
exit(1);
|
|
}
|
|
|
|
void MagiskInit::prepare_data() const noexcept {
|
|
LOGD("Setup data tmp\n");
|
|
xmkdir("/data", 0755);
|
|
xmount("magisk", "/data", "tmpfs", 0, "mode=755");
|
|
|
|
cp_afc("/init", REDIR_PATH);
|
|
cp_afc("/.backup", "/data/.backup");
|
|
cp_afc("/overlay.d", "/data/overlay.d");
|
|
}
|
|
|
|
void MagiskInit::setup_tmp(const char *path) const noexcept {
|
|
LOGD("Setup Magisk tmp at %s\n", path);
|
|
chdir("/data");
|
|
|
|
xmkdir(INTLROOT, 0711);
|
|
xmkdir(DEVICEDIR, 0711);
|
|
xmkdir(WORKERDIR, 0);
|
|
|
|
mount_preinit_dir();
|
|
|
|
cp_afc(".backup/.magisk", MAIN_CONFIG);
|
|
rm_rf(".backup");
|
|
|
|
// Create applet symlinks
|
|
for (int i = 0; applet_names[i]; ++i)
|
|
xsymlink("./magisk", applet_names[i]);
|
|
xsymlink("./magiskpolicy", "supolicy");
|
|
|
|
xmount(".", path, nullptr, MS_BIND, nullptr);
|
|
|
|
chdir(path);
|
|
|
|
// Prepare worker
|
|
xmount("magisk", WORKERDIR, "tmpfs", 0, "mode=755");
|
|
|
|
// Use isolated devpts if kernel support
|
|
if (access("/dev/pts/ptmx", F_OK) == 0) {
|
|
xmkdirs(SHELLPTS, 0755);
|
|
xmount("devpts", SHELLPTS, "devpts", MS_NOSUID | MS_NOEXEC, "newinstance");
|
|
xmount(nullptr, SHELLPTS, nullptr, MS_PRIVATE, nullptr);
|
|
if (access(SHELLPTS "/ptmx", F_OK)) {
|
|
umount2(SHELLPTS, MNT_DETACH);
|
|
rmdir(SHELLPTS);
|
|
}
|
|
}
|
|
|
|
chdir("/");
|
|
}
|