mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-25 09:07:50 +00:00
Refactor module and node implementation
This commit is contained in:
parent
78ca682bc5
commit
92077ebe53
@ -290,7 +290,7 @@ static void post_fs_data() {
|
|||||||
|
|
||||||
early_abort:
|
early_abort:
|
||||||
// We still do magic mount because root itself might need it
|
// We still do magic mount because root itself might need it
|
||||||
magic_mount();
|
load_modules();
|
||||||
boot_state |= FLAG_POST_FS_DATA_DONE;
|
boot_state |= FLAG_POST_FS_DATA_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ std::string read_certificate(int fd, int version = -1);
|
|||||||
|
|
||||||
// Module stuffs
|
// Module stuffs
|
||||||
void handle_modules();
|
void handle_modules();
|
||||||
void magic_mount();
|
void load_modules();
|
||||||
void disable_modules();
|
void disable_modules();
|
||||||
void remove_modules();
|
void remove_modules();
|
||||||
void exec_module_scripts(const char *stage);
|
void exec_module_scripts(const char *stage);
|
||||||
|
@ -9,28 +9,12 @@
|
|||||||
#include <resetprop.hpp>
|
#include <resetprop.hpp>
|
||||||
|
|
||||||
#include "core.hpp"
|
#include "core.hpp"
|
||||||
|
#include "node.hpp"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#define VLOGD(tag, from, to) LOGD("%-8s: %s <- %s\n", tag, to, from)
|
#define VLOGD(tag, from, to) LOGD("%-8s: %s <- %s\n", tag, to, from)
|
||||||
|
|
||||||
#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_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;
|
|
||||||
|
|
||||||
template<class T> static bool isa(node_entry *node);
|
|
||||||
static int bind_mount(const char *reason, const char *from, const char *to) {
|
static int bind_mount(const char *reason, const char *from, const char *to) {
|
||||||
int ret = xmount(from, to, nullptr, MS_BIND | MS_REC, nullptr);
|
int ret = xmount(from, to, nullptr, MS_BIND | MS_REC, nullptr);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
@ -38,349 +22,22 @@ static int bind_mount(const char *reason, const char *from, const char *to) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
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; }
|
|
||||||
|
|
||||||
class node_entry {
|
|
||||||
public:
|
|
||||||
virtual ~node_entry() = default;
|
|
||||||
|
|
||||||
// Node info
|
|
||||||
bool is_dir() { return file_type() == DT_DIR; }
|
|
||||||
bool is_lnk() { return file_type() == DT_LNK; }
|
|
||||||
bool is_reg() { return file_type() == DT_REG; }
|
|
||||||
uint8_t type() { return node_type; }
|
|
||||||
const string &name() { return _name; }
|
|
||||||
|
|
||||||
// Don't call the following two functions before prepare
|
|
||||||
const string &node_path();
|
|
||||||
string mirror_path() { return mirror_dir + node_path(); }
|
|
||||||
|
|
||||||
// Tree methods
|
|
||||||
dir_node *parent() { return _parent; }
|
|
||||||
void merge(node_entry *other);
|
|
||||||
|
|
||||||
virtual void mount() = 0;
|
|
||||||
|
|
||||||
static string module_mnt;
|
|
||||||
static string mirror_dir;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
template<class T>
|
|
||||||
node_entry(const char *name, uint8_t file_type, T*)
|
|
||||||
: _name(name), _file_type(file_type), node_type(type_id<T>()) {}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
explicit node_entry(T*) : _file_type(0), node_type(type_id<T>()) {}
|
|
||||||
|
|
||||||
void create_and_mount(const char *reason, const string &src);
|
|
||||||
|
|
||||||
// Use top bit of _file_type for node exist status
|
|
||||||
bool exist() { return static_cast<bool>(_file_type & (1 << 7)); }
|
|
||||||
void set_exist(bool b) { if (b) _file_type |= (1 << 7); else _file_type &= ~(1 << 7); }
|
|
||||||
uint8_t file_type() { return static_cast<uint8_t>(_file_type & ~(1 << 7)); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class dir_node;
|
|
||||||
|
|
||||||
static bool should_be_tmpfs(node_entry *child);
|
|
||||||
|
|
||||||
// Node properties
|
|
||||||
string _name;
|
|
||||||
uint8_t _file_type;
|
|
||||||
uint8_t node_type;
|
|
||||||
|
|
||||||
dir_node *_parent = nullptr;
|
|
||||||
|
|
||||||
// Cache, it should only be used within prepare
|
|
||||||
string _node_path;
|
|
||||||
};
|
|
||||||
|
|
||||||
class dir_node : public node_entry {
|
|
||||||
public:
|
|
||||||
friend void node_entry::merge(node_entry *other);
|
|
||||||
|
|
||||||
using map_type = map<string_view, node_entry *>;
|
|
||||||
using iterator = map_type::iterator;
|
|
||||||
|
|
||||||
~dir_node() override {
|
|
||||||
for (auto &it : children)
|
|
||||||
delete it.second;
|
|
||||||
children.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void collect_files(const char *module, int dfd);
|
|
||||||
|
|
||||||
// Return true to indicate need to upgrade to skeleton
|
|
||||||
bool prepare(bool should_skip_mirror=false);
|
|
||||||
|
|
||||||
// Default directory mount logic
|
|
||||||
void mount() override {
|
|
||||||
for (auto &pair : children)
|
|
||||||
pair.second->mount();
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************
|
|
||||||
* Tree Methods
|
|
||||||
***************/
|
|
||||||
|
|
||||||
bool is_empty() { return children.empty(); }
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
T *child(string_view name) { return iterator_to_node<T>(children.find(name)); }
|
|
||||||
|
|
||||||
// Lazy val
|
|
||||||
root_node *root() {
|
|
||||||
if (!_root)
|
|
||||||
_root = _parent->root();
|
|
||||||
return _root;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return child with name or nullptr
|
|
||||||
node_entry *extract(string_view name);
|
|
||||||
|
|
||||||
// Return false if rejected
|
|
||||||
bool insert(node_entry *node) {
|
|
||||||
auto fn = [=](auto) { return node; };
|
|
||||||
return node && iterator_to_node(insert(node->_name, node->node_type, fn));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return inserted node or null if rejected
|
|
||||||
template<class T, class ...Args>
|
|
||||||
T *emplace(string_view name, Args &&...args) {
|
|
||||||
auto fn = [&](auto) { return new T(std::forward<Args>(args)...); };
|
|
||||||
return iterator_to_node<T>(insert(name, type_id<T>(), fn));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return inserted node, existing node with same rank, or null if rejected
|
|
||||||
template<class T, class ...Args>
|
|
||||||
T *emplace_or_get(string_view name, Args &&...args) {
|
|
||||||
auto fn = [&](auto) { return new T(std::forward<Args>(args)...); };
|
|
||||||
return iterator_to_node<T>(insert(name, type_id<T>(), fn, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return upgraded node or null if rejected
|
|
||||||
template<class T, class ...Args>
|
|
||||||
T *upgrade(string_view name, Args &...args) {
|
|
||||||
return iterator_to_node<T>(upgrade<T>(children.find(name), args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool skip_mirror() const { return _skip_mirror; }
|
|
||||||
void set_skip_mirror(bool b) { _skip_mirror = b; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
template<class T>
|
|
||||||
dir_node(const char *name, uint8_t file_type, T *self) : node_entry(name, file_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) {
|
|
||||||
merge(node);
|
|
||||||
if constexpr (std::is_same_v<T, root_node>)
|
|
||||||
_root = self;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
dir_node(const char *name, T *self) : dir_node(name, DT_DIR, self) {}
|
|
||||||
|
|
||||||
template<class T = node_entry>
|
|
||||||
T *iterator_to_node(iterator it) {
|
|
||||||
return static_cast<T*>(it == children.end() ? nullptr : it->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emplace insert a new node, or upgrade if the requested type has a higher rank.
|
|
||||||
// Return iterator to new node or end() if insertion is rejected.
|
|
||||||
// If get_same is true and a node with the same rank exists, it will return that node instead.
|
|
||||||
// fn is the node construction callback. Signature: (node_ent *&) -> node_ent *
|
|
||||||
// fn gets a reference to the existing node pointer and returns a new node object.
|
|
||||||
// Input is null when there is no existing node. If returns null, the insertion is rejected.
|
|
||||||
// If fn consumes the input, it should set the reference to null.
|
|
||||||
template<typename Func>
|
|
||||||
iterator insert(iterator it, uint8_t type, const Func &fn, bool get_same);
|
|
||||||
|
|
||||||
template<typename Func>
|
|
||||||
iterator insert(string_view name, uint8_t type, const Func &fn, bool get_same = false) {
|
|
||||||
return insert(children.find(name), type, fn, get_same);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class To, class From = node_entry, class ...Args>
|
|
||||||
iterator upgrade(iterator it, Args &&...args);
|
|
||||||
|
|
||||||
// dir nodes host children
|
|
||||||
map_type children;
|
|
||||||
|
|
||||||
// Root node lookup cache
|
|
||||||
root_node *_root = nullptr;
|
|
||||||
|
|
||||||
// Skip binding mirror for this directory
|
|
||||||
bool _skip_mirror = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
class root_node : public dir_node {
|
|
||||||
public:
|
|
||||||
explicit root_node(const char *name) : dir_node(name, this), prefix("") {
|
|
||||||
set_exist(true);
|
|
||||||
}
|
|
||||||
explicit root_node(node_entry *node) : dir_node(node, this), prefix("/system") {
|
|
||||||
set_exist(true);
|
|
||||||
}
|
|
||||||
const char * const prefix;
|
|
||||||
};
|
|
||||||
|
|
||||||
class inter_node : public dir_node {
|
|
||||||
public:
|
|
||||||
inter_node(const char *name, const char *module) : dir_node(name, this), module(module) {}
|
|
||||||
private:
|
|
||||||
const char *module;
|
|
||||||
friend class module_node;
|
|
||||||
};
|
|
||||||
|
|
||||||
class module_node : public node_entry {
|
|
||||||
public:
|
|
||||||
module_node(const char *module, dirent *entry)
|
|
||||||
: node_entry(entry->d_name, entry->d_type, this), module(module) {}
|
|
||||||
|
|
||||||
module_node(node_entry *node, const char *module) : node_entry(this), module(module) {
|
|
||||||
merge(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit module_node(inter_node *node) : module_node(node, node->module) {}
|
|
||||||
|
|
||||||
void mount() override;
|
|
||||||
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 {
|
|
||||||
create_and_mount("mirror", mirror_path());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class tmpfs_node : public dir_node {
|
|
||||||
public:
|
|
||||||
explicit tmpfs_node(node_entry *node);
|
|
||||||
void mount() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Poor man's dynamic cast without RTTI
|
|
||||||
template<class T>
|
|
||||||
static bool isa(node_entry *node) {
|
|
||||||
return node && (node->type() & type_id<T>());
|
|
||||||
}
|
|
||||||
template<class T>
|
|
||||||
static T *dyn_cast(node_entry *node) {
|
|
||||||
return isa<T>(node) ? static_cast<T*>(node) : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
string node_entry::module_mnt;
|
string node_entry::module_mnt;
|
||||||
string node_entry::mirror_dir;
|
string node_entry::mirror_dir;
|
||||||
|
|
||||||
// other will be deleted
|
|
||||||
void node_entry::merge(node_entry *other) {
|
|
||||||
_name.swap(other->_name);
|
|
||||||
_file_type = other->_file_type;
|
|
||||||
_parent = other->_parent;
|
|
||||||
|
|
||||||
// Merge children if both is dir
|
|
||||||
if (auto a = dyn_cast<dir_node>(this)) {
|
|
||||||
if (auto b = dyn_cast<dir_node>(other)) {
|
|
||||||
a->_skip_mirror = b->_skip_mirror;
|
|
||||||
a->children.merge(b->children);
|
|
||||||
for (auto &pair : a->children)
|
|
||||||
pair.second->_parent = a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete other;
|
|
||||||
}
|
|
||||||
|
|
||||||
const string &node_entry::node_path() {
|
|
||||||
if (_parent && _node_path.empty())
|
|
||||||
_node_path = _parent->node_path() + '/' + _name;
|
|
||||||
return _node_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*************************
|
/*************************
|
||||||
* Node Tree Construction
|
* Node Tree Construction
|
||||||
*************************/
|
*************************/
|
||||||
|
|
||||||
template<typename Func>
|
|
||||||
dir_node::iterator dir_node::insert(iterator it, uint8_t type, const Func &fn, bool get_same) {
|
|
||||||
node_entry *node = nullptr;
|
|
||||||
if (it != children.end()) {
|
|
||||||
// Upgrade existing node only if higher rank
|
|
||||||
if (it->second->node_type < type) {
|
|
||||||
node = fn(it->second);
|
|
||||||
if (!node)
|
|
||||||
return children.end();
|
|
||||||
if (it->second)
|
|
||||||
node->merge(it->second);
|
|
||||||
it = children.erase(it);
|
|
||||||
// Minor optimization to make insert O(1) by using hint
|
|
||||||
if (it == children.begin())
|
|
||||||
it = children.emplace(node->_name, node).first;
|
|
||||||
else
|
|
||||||
it = children.emplace_hint(--it, node->_name, node);
|
|
||||||
} else {
|
|
||||||
if (get_same && it->second->node_type != type)
|
|
||||||
return children.end();
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
node = fn(node);
|
|
||||||
if (!node)
|
|
||||||
return children.end();
|
|
||||||
node->_parent = this;
|
|
||||||
it = children.emplace(node->_name, node).first;
|
|
||||||
}
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class To, class From, class... Args>
|
|
||||||
dir_node::iterator dir_node::upgrade(iterator it, Args &&... args) {
|
|
||||||
return insert(it, type_id<To>(), [&](node_entry *&ex) -> node_entry * {
|
|
||||||
if (!ex)
|
|
||||||
return nullptr;
|
|
||||||
if constexpr (!std::is_same_v<From, node_entry>) {
|
|
||||||
// Type check if type is specified
|
|
||||||
if (!isa<From>(ex))
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto node = new To(static_cast<From *>(ex), std::forward<Args>(args)...);
|
|
||||||
ex = nullptr;
|
|
||||||
return node;
|
|
||||||
}, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
node_entry* dir_node::extract(string_view name) {
|
|
||||||
auto it = children.find(name);
|
|
||||||
if (it != children.end()) {
|
|
||||||
auto ret = it->second;
|
|
||||||
children.erase(it);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpfs_node::tmpfs_node(node_entry *node) : dir_node(node, this) {
|
tmpfs_node::tmpfs_node(node_entry *node) : dir_node(node, this) {
|
||||||
|
if (!skip_mirror) {
|
||||||
string mirror = mirror_path();
|
string mirror = mirror_path();
|
||||||
if (!skip_mirror()) {
|
|
||||||
if (auto dir = open_dir(mirror.data())) {
|
if (auto dir = open_dir(mirror.data())) {
|
||||||
set_exist(true);
|
set_exist(true);
|
||||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||||
if (entry->d_type == DT_DIR) {
|
if (entry->d_type == DT_DIR) {
|
||||||
// create a dummy inter_node to upgrade later
|
// create a dummy inter_node to upgrade later
|
||||||
emplace<inter_node>(entry->d_name, entry->d_name, "mirror");
|
emplace<inter_node>(entry->d_name, entry->d_name, "");
|
||||||
} else {
|
} else {
|
||||||
// Insert mirror nodes
|
// Insert mirror nodes
|
||||||
emplace<mirror_node>(entry->d_name, entry);
|
emplace<mirror_node>(entry->d_name, entry);
|
||||||
@ -396,34 +53,16 @@ tmpfs_node::tmpfs_node(node_entry *node) : dir_node(node, this) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to upgrade to tmpfs node if any child:
|
bool dir_node::prepare() {
|
||||||
// - Target does not exist
|
bool to_tmpfs = skip_mirror;
|
||||||
// - Source or target is a symlink (since we cannot bind mount link)
|
|
||||||
bool node_entry::should_be_tmpfs(node_entry *child) {
|
|
||||||
struct stat st{};
|
|
||||||
if (lstat(child->node_path().data(), &st) != 0) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
child->set_exist(true);
|
|
||||||
if (child->is_lnk() || S_ISLNK(st.st_mode))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dir_node::prepare(bool should_skip_mirror) {
|
|
||||||
if (!skip_mirror() && should_skip_mirror) {
|
|
||||||
set_skip_mirror(true);
|
|
||||||
}
|
|
||||||
bool to_tmpfs = false;
|
|
||||||
if (!exist()) {
|
if (!exist()) {
|
||||||
// If not exist, we need to create it by mounting tmpfs
|
// If not exist, we need to create it by mounting tmpfs
|
||||||
to_tmpfs = true;
|
to_tmpfs = true;
|
||||||
set_exist(true);
|
set_exist(true);
|
||||||
}
|
}
|
||||||
for (auto it = children.begin(); it != children.end();) {
|
for (auto it = children.begin(); it != children.end();) {
|
||||||
if (should_be_tmpfs(it->second)) {
|
if (require_tmpfs_upgrade(it->second)) {
|
||||||
if (node_type > type_id<tmpfs_node>()) {
|
if (_node_type > type_id<tmpfs_node>()) {
|
||||||
// Upgrade will fail, remove the unsupported child node
|
// Upgrade will fail, remove the unsupported child node
|
||||||
LOGW("Unable to add: %s, skipped\n", it->second->node_path().data());
|
LOGW("Unable to add: %s, skipped\n", it->second->node_path().data());
|
||||||
delete it->second;
|
delete it->second;
|
||||||
@ -432,7 +71,7 @@ bool dir_node::prepare(bool should_skip_mirror) {
|
|||||||
}
|
}
|
||||||
// Tell parent to upgrade self to tmpfs
|
// Tell parent to upgrade self to tmpfs
|
||||||
to_tmpfs = true;
|
to_tmpfs = true;
|
||||||
// If child is inter_node and it does not (need to) exist, upgrade to module
|
// If child is inter_node and it does not exist, upgrade to module
|
||||||
if (auto dn = dyn_cast<inter_node>(it->second)) {
|
if (auto dn = dyn_cast<inter_node>(it->second)) {
|
||||||
if (!dn->exist()) {
|
if (!dn->exist()) {
|
||||||
if (auto nit = upgrade<module_node, inter_node>(it); nit != children.end()) {
|
if (auto nit = upgrade<module_node, inter_node>(it); nit != children.end()) {
|
||||||
@ -442,25 +81,30 @@ bool dir_node::prepare(bool should_skip_mirror) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (auto dn = dyn_cast<dir_node>(it->second); dn && dn->prepare(skip_mirror())) {
|
if (auto dn = dyn_cast<dir_node>(it->second)) {
|
||||||
|
if (skip_mirror) {
|
||||||
|
dn->skip_mirror = true;
|
||||||
|
}
|
||||||
|
if (dn->prepare()) {
|
||||||
// Upgrade child to tmpfs
|
// Upgrade child to tmpfs
|
||||||
it = upgrade<tmpfs_node>(it);
|
it = upgrade<tmpfs_node>(it);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
next_node:
|
next_node:
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
return to_tmpfs || skip_mirror();
|
return to_tmpfs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dir_node::collect_files(const char *module, int dfd) {
|
void dir_node::collect_module_files(const char *module, int dfd) {
|
||||||
auto dir = xopen_dir(xopenat(dfd, _name.data(), O_RDONLY | O_CLOEXEC));
|
auto dir = xopen_dir(xopenat(dfd, name().data(), O_RDONLY | O_CLOEXEC));
|
||||||
if (!dir)
|
if (!dir)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||||
inter_node *dn;
|
inter_node *dn;
|
||||||
if (entry->d_name == ".replace"sv) {
|
if (entry->d_name == ".replace"sv) {
|
||||||
set_skip_mirror(true);
|
skip_mirror = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,7 +115,7 @@ void dir_node::collect_files(const char *module, int dfd) {
|
|||||||
dn = dyn_cast<inter_node>(it->second);
|
dn = dyn_cast<inter_node>(it->second);
|
||||||
}
|
}
|
||||||
if (dn) {
|
if (dn) {
|
||||||
dn->collect_files(module, dirfd(dir.get()));
|
dn->collect_module_files(module, dirfd(dir.get()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
emplace<module_node>(entry->d_name, module, entry);
|
emplace<module_node>(entry->d_name, module, entry);
|
||||||
@ -518,10 +162,9 @@ void tmpfs_node::mount() {
|
|||||||
else
|
else
|
||||||
getattr(parent()->node_path().data(), &a);
|
getattr(parent()->node_path().data(), &a);
|
||||||
if (!isa<tmpfs_node>(parent())) {
|
if (!isa<tmpfs_node>(parent())) {
|
||||||
// We don't need another layer of tmpfs if parent is skel
|
|
||||||
auto worker_dir = MAGISKTMP + "/" WORKERDIR + dest;
|
auto worker_dir = MAGISKTMP + "/" WORKERDIR + dest;
|
||||||
mkdirs(worker_dir.data(), 0);
|
mkdirs(worker_dir.data(), 0);
|
||||||
create_and_mount(skip_mirror() ? "replace" : "tmpfs", worker_dir);
|
create_and_mount(skip_mirror ? "replace" : "tmpfs", worker_dir);
|
||||||
} else {
|
} else {
|
||||||
// We don't need another layer of tmpfs if parent is tmpfs
|
// We don't need another layer of tmpfs if parent is tmpfs
|
||||||
mkdir(dest.data(), 0);
|
mkdir(dest.data(), 0);
|
||||||
@ -560,7 +203,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void inject_magisk_bins(root_node *system) {
|
static void inject_magisk_bins(root_node *system) {
|
||||||
auto bin = system->child<inter_node>("bin");
|
auto bin = system->get_child<inter_node>("bin");
|
||||||
if (!bin) {
|
if (!bin) {
|
||||||
bin = new inter_node("bin", "");
|
bin = new inter_node("bin", "");
|
||||||
system->insert(bin);
|
system->insert(bin);
|
||||||
@ -594,7 +237,7 @@ if (access("/system/bin/app_process" #bit, F_OK) == 0) {
|
|||||||
bind_mount("zygisk", zbin.data(), "/system/bin/app_process" #bit); \
|
bind_mount("zygisk", zbin.data(), "/system/bin/app_process" #bit); \
|
||||||
}
|
}
|
||||||
|
|
||||||
void magic_mount() {
|
void load_modules() {
|
||||||
node_entry::mirror_dir = MAGISKTMP + "/" MIRRDIR;
|
node_entry::mirror_dir = MAGISKTMP + "/" MIRRDIR;
|
||||||
node_entry::module_mnt = MAGISKTMP + "/" MODULEMNT "/";
|
node_entry::module_mnt = MAGISKTMP + "/" MODULEMNT "/";
|
||||||
|
|
||||||
@ -628,7 +271,7 @@ void magic_mount() {
|
|||||||
LOGI("%s: loading mount files\n", module);
|
LOGI("%s: loading mount files\n", module);
|
||||||
b[-1] = '\0';
|
b[-1] = '\0';
|
||||||
int fd = xopen(buf, O_RDONLY | O_CLOEXEC);
|
int fd = xopen(buf, O_RDONLY | O_CLOEXEC);
|
||||||
system->collect_files(module, fd);
|
system->collect_module_files(module, fd);
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
if (MAGISKTMP != "/sbin" || !str_contains(getenv("PATH") ?: "", "/sbin")) {
|
if (MAGISKTMP != "/sbin" || !str_contains(getenv("PATH") ?: "", "/sbin")) {
|
||||||
@ -662,6 +305,10 @@ void magic_mount() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/************************
|
||||||
|
* Filesystem operations
|
||||||
|
************************/
|
||||||
|
|
||||||
static void prepare_modules() {
|
static void prepare_modules() {
|
||||||
// Upgrade modules
|
// Upgrade modules
|
||||||
if (auto dir = open_dir(MODULEUPGRADE); dir) {
|
if (auto dir = open_dir(MODULEUPGRADE); dir) {
|
||||||
|
341
native/src/core/node.hpp
Normal file
341
native/src/core/node.hpp
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
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_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;
|
||||||
|
|
||||||
|
// Poor man's dynamic cast without RTTI
|
||||||
|
template<class T> static bool isa(node_entry *node);
|
||||||
|
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; }
|
||||||
|
|
||||||
|
class node_entry {
|
||||||
|
public:
|
||||||
|
virtual ~node_entry() = default;
|
||||||
|
|
||||||
|
// Node info
|
||||||
|
bool is_dir() const { return file_type() == DT_DIR; }
|
||||||
|
bool is_lnk() const { return file_type() == DT_LNK; }
|
||||||
|
bool is_reg() const { return file_type() == DT_REG; }
|
||||||
|
const string &name() const { return _name; }
|
||||||
|
dir_node *parent() const { return _parent; }
|
||||||
|
|
||||||
|
// Don't call the following two functions before prepare
|
||||||
|
const string &node_path();
|
||||||
|
string mirror_path() { return mirror_dir + node_path(); }
|
||||||
|
|
||||||
|
virtual void mount() = 0;
|
||||||
|
|
||||||
|
static string module_mnt;
|
||||||
|
static string mirror_dir;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template<class T>
|
||||||
|
node_entry(const char *name, uint8_t file_type, T*)
|
||||||
|
: _name(name), _file_type(file_type), _node_type(type_id<T>()) {}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
explicit node_entry(T*) : _file_type(0), _node_type(type_id<T>()) {}
|
||||||
|
|
||||||
|
void consume(node_entry *other);
|
||||||
|
void create_and_mount(const char *reason, const string &src);
|
||||||
|
|
||||||
|
// Use top bit of _file_type for node exist status
|
||||||
|
bool exist() const { return static_cast<bool>(_file_type & (1 << 7)); }
|
||||||
|
void set_exist(bool b) { if (b) _file_type |= (1 << 7); else _file_type &= ~(1 << 7); }
|
||||||
|
uint8_t file_type() const { return static_cast<uint8_t>(_file_type & ~(1 << 7)); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class dir_node;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
friend bool isa(node_entry *node);
|
||||||
|
|
||||||
|
// Node properties
|
||||||
|
string _name;
|
||||||
|
uint8_t _file_type;
|
||||||
|
const uint8_t _node_type;
|
||||||
|
|
||||||
|
dir_node *_parent = nullptr;
|
||||||
|
|
||||||
|
// Cache, it should only be used within prepare
|
||||||
|
string _node_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
class dir_node : public node_entry {
|
||||||
|
public:
|
||||||
|
friend void node_entry::consume(node_entry *other);
|
||||||
|
|
||||||
|
using map_type = map<string_view, node_entry *>;
|
||||||
|
using iterator = map_type::iterator;
|
||||||
|
|
||||||
|
~dir_node() override {
|
||||||
|
for (auto &it : children)
|
||||||
|
delete it.second;
|
||||||
|
children.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse through module directories to generate a tree of module files
|
||||||
|
void collect_module_files(const char *module, int dfd);
|
||||||
|
|
||||||
|
// Traverse through the real filesystem and prepare the tree for magic mount.
|
||||||
|
// Return true to indicate that this node needs to be upgraded to tmpfs_node.
|
||||||
|
bool prepare();
|
||||||
|
|
||||||
|
// Default directory mount logic
|
||||||
|
void mount() override {
|
||||||
|
for (auto &pair : children)
|
||||||
|
pair.second->mount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************
|
||||||
|
* Tree Methods
|
||||||
|
***************/
|
||||||
|
|
||||||
|
bool is_empty() { return children.empty(); }
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
T *get_child(string_view name) { return iterator_to_node<T>(children.find(name)); }
|
||||||
|
|
||||||
|
root_node *root() {
|
||||||
|
if (!_root)
|
||||||
|
_root = _parent->root();
|
||||||
|
return _root;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return child with name or nullptr
|
||||||
|
node_entry *extract(string_view name) {
|
||||||
|
auto it = children.find(name);
|
||||||
|
if (it != children.end()) {
|
||||||
|
auto ret = it->second;
|
||||||
|
children.erase(it);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return false if rejected
|
||||||
|
bool insert(node_entry *node) {
|
||||||
|
auto fn = [=](auto) { return node; };
|
||||||
|
return node && iterator_to_node(insert(node->_name, node->_node_type, fn));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return inserted node or null if rejected
|
||||||
|
template<class T, class ...Args>
|
||||||
|
T *emplace(string_view name, Args &&...args) {
|
||||||
|
auto fn = [&](auto) { return new T(std::forward<Args>(args)...); };
|
||||||
|
return iterator_to_node<T>(insert(name, type_id<T>(), fn));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return upgraded node or null if rejected
|
||||||
|
template<class T, class ...Args>
|
||||||
|
T *upgrade(string_view name, Args &...args) {
|
||||||
|
return iterator_to_node<T>(upgrade<T>(children.find(name), args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template<class T>
|
||||||
|
dir_node(const char *name, T *self) : node_entry(name, DT_DIR, 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) {
|
||||||
|
consume(node);
|
||||||
|
if constexpr (std::is_same_v<T, root_node>)
|
||||||
|
_root = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T = node_entry>
|
||||||
|
T *iterator_to_node(iterator it) {
|
||||||
|
return static_cast<T*>(it == children.end() ? nullptr : it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Func>
|
||||||
|
iterator insert(string_view name, uint8_t type, const Func &fn) {
|
||||||
|
return insert_at(children.find(name), type, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emplace insert a new node, or upgrade if the requested type has a higher rank.
|
||||||
|
// Return iterator to the new/upgraded node, or end() if insertion is rejected.
|
||||||
|
// fn is the node builder function. Signature: (node_entry *&) -> node_entry *
|
||||||
|
// fn gets a reference to the existing node pointer and returns a new node object.
|
||||||
|
// Input is null when there is no existing node. If returns null, the insertion is rejected.
|
||||||
|
// If fn consumes the input, it should set the reference to null.
|
||||||
|
template<typename Builder>
|
||||||
|
iterator insert_at(iterator it, uint8_t type, const Builder &builder) {
|
||||||
|
node_entry *node = nullptr;
|
||||||
|
if (it != children.end()) {
|
||||||
|
// Upgrade existing node only if higher rank
|
||||||
|
if (it->second->_node_type < type) {
|
||||||
|
node = builder(it->second);
|
||||||
|
if (!node)
|
||||||
|
return children.end();
|
||||||
|
if (it->second)
|
||||||
|
node->consume(it->second);
|
||||||
|
it = children.erase(it);
|
||||||
|
// Minor optimization to make insert O(1) by using hint
|
||||||
|
if (it == children.begin())
|
||||||
|
it = children.emplace(node->_name, node).first;
|
||||||
|
else
|
||||||
|
it = children.emplace_hint(--it, node->_name, node);
|
||||||
|
} else {
|
||||||
|
return children.end();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node = builder(node);
|
||||||
|
if (!node)
|
||||||
|
return children.end();
|
||||||
|
node->_parent = this;
|
||||||
|
it = children.emplace(node->_name, node).first;
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class To, class From = node_entry, class ...Args>
|
||||||
|
iterator upgrade(iterator it, Args &&...args) {
|
||||||
|
return insert_at(it, type_id<To>(), [&](node_entry *&ex) -> node_entry * {
|
||||||
|
if (!ex)
|
||||||
|
return nullptr;
|
||||||
|
if constexpr (!std::is_same_v<From, node_entry>) {
|
||||||
|
// Type check if type is specified
|
||||||
|
if (!isa<From>(ex))
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto node = new To(static_cast<From *>(ex), std::forward<Args>(args)...);
|
||||||
|
ex = nullptr;
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// dir nodes host children
|
||||||
|
map_type children;
|
||||||
|
|
||||||
|
// Skip binding mirror for this directory
|
||||||
|
bool skip_mirror = false;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Root node lookup cache
|
||||||
|
root_node *_root = nullptr;
|
||||||
|
|
||||||
|
// We need to upgrade to tmpfs node if any child:
|
||||||
|
// - Target does not exist
|
||||||
|
// - Source or target is a symlink (since we cannot bind mount link)
|
||||||
|
bool require_tmpfs_upgrade(node_entry *child) {
|
||||||
|
struct stat st{};
|
||||||
|
if (lstat(child->node_path().data(), &st) != 0) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
child->set_exist(true);
|
||||||
|
if (child->is_lnk() || S_ISLNK(st.st_mode))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class root_node : public dir_node {
|
||||||
|
public:
|
||||||
|
explicit root_node(const char *name) : dir_node(name, this), prefix("") {
|
||||||
|
set_exist(true);
|
||||||
|
}
|
||||||
|
explicit root_node(node_entry *node) : dir_node(node, this), prefix("/system") {
|
||||||
|
set_exist(true);
|
||||||
|
}
|
||||||
|
const char * const prefix;
|
||||||
|
};
|
||||||
|
|
||||||
|
class inter_node : public dir_node {
|
||||||
|
public:
|
||||||
|
inter_node(const char *name, const char *module) : dir_node(name, this), module(module) {}
|
||||||
|
private:
|
||||||
|
const char *module;
|
||||||
|
friend class module_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
class module_node : public node_entry {
|
||||||
|
public:
|
||||||
|
module_node(const char *module, dirent *entry)
|
||||||
|
: node_entry(entry->d_name, entry->d_type, this), module(module) {}
|
||||||
|
|
||||||
|
module_node(node_entry *node, const char *module) : node_entry(this), module(module) {
|
||||||
|
consume(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit module_node(inter_node *node) : module_node(node, node->module) {}
|
||||||
|
|
||||||
|
void mount() override;
|
||||||
|
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 {
|
||||||
|
create_and_mount("mirror", mirror_path());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class tmpfs_node : public dir_node {
|
||||||
|
public:
|
||||||
|
explicit tmpfs_node(node_entry *node);
|
||||||
|
void mount() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
static bool isa(node_entry *node) {
|
||||||
|
return node && (node->_node_type & type_id<T>());
|
||||||
|
}
|
||||||
|
template<class T>
|
||||||
|
static T *dyn_cast(node_entry *node) {
|
||||||
|
return isa<T>(node) ? static_cast<T*>(node) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const string &node_entry::node_path() {
|
||||||
|
if (_parent && _node_path.empty())
|
||||||
|
_node_path = _parent->node_path() + '/' + _name;
|
||||||
|
return _node_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void node_entry::consume(node_entry *other) {
|
||||||
|
_name.swap(other->_name);
|
||||||
|
_file_type = other->_file_type;
|
||||||
|
_parent = other->_parent;
|
||||||
|
|
||||||
|
// Merge children if both is dir
|
||||||
|
if (auto a = dyn_cast<dir_node>(this)) {
|
||||||
|
if (auto b = dyn_cast<dir_node>(other)) {
|
||||||
|
a->skip_mirror = b->skip_mirror;
|
||||||
|
a->children.merge(b->children);
|
||||||
|
for (auto &pair : a->children)
|
||||||
|
pair.second->_parent = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete other;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user