Remove unnecessary mirror for magic mount

Mirror was previously used for accessing the original file during
magic mount when we are using a tmpfs to cover the target. However,
since we introduce atomic mount, we switch all tmpfs mount in
worker and then move to the target at once. It means that we can
still access the original file when we are constructing the tmpfs
mount point. Thus we no longer need mirror.
This commit is contained in:
LoveSy 2024-01-05 00:42:33 +08:00 committed by John Wu
parent d20b30c771
commit 92b305a389
3 changed files with 40 additions and 93 deletions

View File

@ -19,18 +19,10 @@ bool zygisk_enabled = false;
/*********
* Setup *
*********/
static bool rec_mount(const std::string_view from, const std::string_view to) {
return !xmkdirs(to.data(), 0755) &&
// recursively bind mount to mirror dir, rootfs will fail before 3.12 kernel
// because of MS_NOUSER
!mount(from.data(), to.data(), nullptr, MS_BIND | MS_REC, nullptr);
}
static void mount_mirrors() {
LOGI("* Mounting mirrors\n");
static void setup_mounts() {
LOGI("* Magic mount setup\n");
auto self_mount_info = parse_mount_info("self");
char path[64];
char path[PATH_MAX];
// Bind remount module root to clear nosuid
if (access(SECURE_DIR, F_OK) == 0 || SDK_INT < 24) {
@ -65,8 +57,9 @@ static void mount_mirrors() {
if (!rw) continue;
string preinit_dir = resolve_preinit_dir(info.target.data());
xmkdir(preinit_dir.data(), 0700);
if ((mounted = rec_mount(preinit_dir, path))) {
xmount(nullptr, path, nullptr, MS_UNBINDABLE, nullptr);
xmkdirs(path, 0755);
mounted = xmount(preinit_dir.data(), path, nullptr, MS_BIND, nullptr) == 0;
if (mounted) {
break;
}
}
@ -81,27 +74,6 @@ static void mount_mirrors() {
ssprintf(path, sizeof(path), "%s/" WORKERDIR, get_magisk_tmp());
xmount("worker", path, "tmpfs", 0, "mode=755");
xmount(nullptr, path, nullptr, MS_PRIVATE, nullptr);
// Recursively bind mount / to mirror dir
// Keep mirror shared so that mounting during post-fs-data will be propagated
if (auto mirror_dir = get_magisk_tmp() + "/"s MIRRDIR; !rec_mount("/", mirror_dir)) {
LOGI("fallback to mount subtree\n");
// create new a bind mount for easy make private
xmount(mirror_dir.data(), mirror_dir.data(), nullptr, MS_BIND, nullptr);
// rootfs may fail, fallback to bind mount each mount point
set<string, greater<>> mounted_dirs {{ get_magisk_tmp() }};
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);
last_mount != mounted_dirs.end() && info.target.starts_with(*last_mount + '/')) {
continue;
}
if (rec_mount(info.target, mirror_dir + info.target)) {
LOGD("%-8s: %s <- %s\n", "rbind", (mirror_dir + info.target).data(), info.target.data());
mounted_dirs.insert(info.target);
}
}
}
}
string find_preinit_device() {
@ -310,7 +282,7 @@ bool MagiskD::post_fs_data() const {
LOGI("** post-fs-data mode running\n");
unlock_blocks();
mount_mirrors();
setup_mounts();
prune_su_access();
bool safe_mode = false;
@ -341,15 +313,7 @@ bool MagiskD::post_fs_data() const {
}
early_abort:
auto mirror_dir = get_magisk_tmp() + "/"s MIRRDIR;
// make mirror dir as a private mount so that it won't be affected by magic mount
LOGD("make %s private\n", mirror_dir.data());
xmount(nullptr, mirror_dir.data(), nullptr, MS_PRIVATE | MS_REC, nullptr);
// We still do magic mount because root itself might need it
load_modules();
// make mirror dir as a shared mount to make magisk --stop work for other ns
xmount(nullptr, mirror_dir.data(), nullptr, MS_SHARED | MS_REC, nullptr);
LOGD("make %s shared\n", mirror_dir.data());
return safe_mode;
}

View File

@ -27,24 +27,18 @@ static int bind_mount(const char *reason, const char *from, const char *to) {
*************************/
tmpfs_node::tmpfs_node(node_entry *node) : dir_node(node, this) {
if (!skip_mirror()) {
string mirror = mirror_path();
if (auto dir = open_dir(mirror.data())) {
if (!replace()) {
if (auto dir = open_dir(node_path().data())) {
set_exist(true);
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_type == DT_DIR) {
// create a dummy inter_node to upgrade later
emplace<inter_node>(entry->d_name, entry->d_name);
} else {
// Insert mirror nodes
emplace<mirror_node>(entry->d_name, entry);
}
// create a dummy inter_node to upgrade later
emplace<inter_node>(entry->d_name, entry);
}
}
}
for (auto it = children.begin(); it != children.end(); ++it) {
// Need to upgrade all inter_node children to tmpfs_node
// Upgrade resting inter_node children to tmpfs_node
if (isa<inter_node>(it->second))
it = upgrade<tmpfs_node>(it);
}
@ -52,7 +46,7 @@ tmpfs_node::tmpfs_node(node_entry *node) : dir_node(node, this) {
bool dir_node::prepare() {
// If direct replace or not exist, mount ourselves as tmpfs
bool upgrade_to_tmpfs = skip_mirror() || !exist();
bool upgrade_to_tmpfs = replace() || !exist();
for (auto it = children.begin(); it != children.end();) {
// We also need to upgrade to tmpfs node if any child:
@ -77,9 +71,9 @@ bool dir_node::prepare() {
upgrade_to_tmpfs = true;
}
if (auto dn = dyn_cast<dir_node>(it->second)) {
if (skip_mirror()) {
if (replace()) {
// Propagate skip mirror state to all children
dn->set_skip_mirror(true);
dn->set_replace(true);
}
if (dn->prepare()) {
// Upgrade child to tmpfs
@ -98,7 +92,7 @@ void dir_node::collect_module_files(const char *module, int dfd) {
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_name == ".replace"sv) {
set_skip_mirror(true);
set_replace(true);
continue;
}
@ -141,18 +135,12 @@ void node_entry::create_and_mount(const char *reason, const string &src, bool ro
}
}
void mirror_node::mount() {
create_and_mount("mirror", mirror_path());
}
void module_node::mount() {
std::string path = module + (parent()->root()->prefix + node_path());
string mnt_src = module_mnt + path;
{
string src = MODULEROOT "/" + path;
if (exist()) clone_attr(mirror_path().data(), src.data());
// special case for /system/etc/hosts to ensure it is writable
if (node_path() == "/system/etc/hosts") mnt_src = std::move(src);
if (exist()) clone_attr(node_path().data(), src.data());
}
if (isa<tmpfs_node>(parent())) {
create_and_mount("module", mnt_src);
@ -162,22 +150,23 @@ void module_node::mount() {
}
void tmpfs_node::mount() {
string src = mirror_path();
const char *src_path = access(src.data(), F_OK) == 0 ? src.data() : nullptr;
if (!is_dir()) {
create_and_mount("mirror", node_path());
return;
}
if (!isa<tmpfs_node>(parent())) {
const string &dest = node_path();
auto worker_dir = worker_path();
mkdirs(worker_dir.data(), 0);
bind_mount("tmpfs", worker_dir.data(), worker_dir.data());
clone_attr(src_path ?: parent()->node_path().data(), worker_dir.data());
clone_attr(exist() ? node_path().data() : parent()->node_path().data(), worker_dir.data());
dir_node::mount();
VLOGD(skip_mirror() ? "replace" : "move", worker_dir.data(), dest.data());
xmount(worker_dir.data(), dest.data(), nullptr, MS_MOVE, nullptr);
VLOGD(replace() ? "replace" : "move", worker_dir.data(), node_path().data());
xmount(worker_dir.data(), node_path().data(), nullptr, MS_MOVE, nullptr);
} else {
const string dest = worker_path();
// We don't need another layer of tmpfs if parent is tmpfs
mkdir(dest.data(), 0);
clone_attr(src_path ?: parent()->worker_path().data(), dest.data());
clone_attr(exist() ? node_path().data() : parent()->worker_path().data(), dest.data());
dir_node::mount();
}
}
@ -265,7 +254,6 @@ static void inject_zygisk_libs(root_node *system) {
vector<module_info> *module_list;
void load_modules() {
node_entry::mirror_dir = get_magisk_tmp() + "/"s MIRRDIR;
node_entry::module_mnt = get_magisk_tmp() + "/"s MODULEMNT "/";
auto root = make_unique<root_node>("");

View File

@ -5,18 +5,16 @@
using namespace std;
#define TYPE_MIRROR (1 << 0) /* mount from mirror */
#define TYPE_INTER (1 << 1) /* intermediate node */
#define TYPE_TMPFS (1 << 2) /* replace with tmpfs */
#define TYPE_MODULE (1 << 3) /* mount from module */
#define TYPE_ROOT (1 << 4) /* partition root */
#define TYPE_CUSTOM (1 << 5) /* custom node type overrides all */
#define TYPE_INTER (1 << 0) /* intermediate node */
#define TYPE_TMPFS (1 << 1) /* replace with tmpfs */
#define TYPE_MODULE (1 << 2) /* mount from module */
#define TYPE_ROOT (1 << 3) /* partition root */
#define TYPE_CUSTOM (1 << 4) /* custom node type overrides all */
#define TYPE_DIR (TYPE_INTER|TYPE_TMPFS|TYPE_ROOT)
class node_entry;
class dir_node;
class inter_node;
class mirror_node;
class tmpfs_node;
class module_node;
class root_node;
@ -27,7 +25,6 @@ template<class T> static T *dyn_cast(node_entry *node);
template<class T> uint8_t type_id() { return TYPE_CUSTOM; }
template<> uint8_t type_id<dir_node>() { return TYPE_DIR; }
template<> uint8_t type_id<inter_node>() { return TYPE_INTER; }
template<> uint8_t type_id<mirror_node>() { return TYPE_MIRROR; }
template<> uint8_t type_id<tmpfs_node>() { return TYPE_TMPFS; }
template<> uint8_t type_id<module_node>() { return TYPE_MODULE; }
template<> uint8_t type_id<root_node>() { return TYPE_ROOT; }
@ -47,12 +44,9 @@ public:
const string &node_path();
const string worker_path();
string mirror_path() { return mirror_dir + node_path(); }
virtual void mount() = 0;
inline static string module_mnt;
inline static string mirror_dir;
protected:
template<class T>
@ -174,6 +168,12 @@ protected:
_root = self;
}
template<class T>
dir_node(dirent *entry, T *self) : node_entry(entry->d_name, entry->d_type, self) {
if constexpr (std::is_same_v<T, root_node>)
_root = self;
}
template<class T>
dir_node(node_entry *node, T *self) : node_entry(self) {
if constexpr (std::is_same_v<T, root_node>)
@ -192,8 +192,8 @@ protected:
// Use bit 6 of _file_type
// Skip binding mirror for this directory
bool skip_mirror() const { return static_cast<bool>(_file_type & (1 << 6)); }
void set_skip_mirror(bool b) { if (b) _file_type |= (1 << 6); else _file_type &= ~(1 << 6); }
bool replace() const { return static_cast<bool>(_file_type & (1 << 6)); }
void set_replace(bool b) { if (b) _file_type |= (1 << 6); else _file_type &= ~(1 << 6); }
template<class T = node_entry>
T *iterator_to_node(iterator it) {
@ -273,6 +273,7 @@ public:
class inter_node : public dir_node {
public:
inter_node(const char *name) : dir_node(name, this) {}
inter_node(dirent *entry) : dir_node(entry, this) {}
};
class module_node : public node_entry {
@ -289,13 +290,7 @@ private:
const char *module;
};
// Don't create the following two nodes before prepare
class mirror_node : public node_entry {
public:
explicit mirror_node(dirent *entry) : node_entry(entry->d_name, entry->d_type, this) {}
void mount() override;
};
// Don't create tmpfs_node before prepare
class tmpfs_node : public dir_node {
public:
explicit tmpfs_node(node_entry *node);