mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-24 02:25:28 +00:00
parent
2468f5a6c4
commit
15f155100c
@ -17,13 +17,13 @@ using namespace std;
|
|||||||
#define VLOGI(tag, from, to) LOGI("%-8s: %s\n", tag, to)
|
#define VLOGI(tag, from, to) LOGI("%-8s: %s\n", tag, to)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define TYPE_INTER (1 << 0) /* intermediate node */
|
#define TYPE_MIRROR (1 << 0) /* mount from mirror */
|
||||||
#define TYPE_MIRROR (1 << 1) /* mount from mirror */
|
#define TYPE_INTER (1 << 1) /* intermediate node */
|
||||||
#define TYPE_SKEL (1 << 2) /* replace with tmpfs */
|
#define TYPE_SKEL (1 << 2) /* replace with tmpfs */
|
||||||
#define TYPE_MODULE (1 << 3) /* mount from module */
|
#define TYPE_MODULE (1 << 3) /* mount from module */
|
||||||
#define TYPE_ROOT (1 << 4) /* partition root */
|
#define TYPE_ROOT (1 << 4) /* partition root */
|
||||||
#define TYPE_CUSTOM (1 << 5) /* custom node type overrides all */
|
#define TYPE_CUSTOM (1 << 5) /* custom node type overrides all */
|
||||||
#define TYPE_DIR (TYPE_INTER|TYPE_MIRROR|TYPE_SKEL|TYPE_ROOT)
|
#define TYPE_DIR (TYPE_INTER|TYPE_SKEL|TYPE_ROOT)
|
||||||
|
|
||||||
vector<string> module_list;
|
vector<string> module_list;
|
||||||
|
|
||||||
@ -86,14 +86,14 @@ protected:
|
|||||||
|
|
||||||
/* Use top bit of _file_type for node exist status */
|
/* Use top bit of _file_type for node exist status */
|
||||||
bool exist() { return static_cast<bool>(_file_type & (1 << 7)); }
|
bool exist() { return static_cast<bool>(_file_type & (1 << 7)); }
|
||||||
void set_exist() { _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)); }
|
uint8_t file_type() { return static_cast<uint8_t>(_file_type & ~(1 << 7)); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend void merge_node(node_entry *a, node_entry *b);
|
friend void merge_node(node_entry *a, node_entry *b);
|
||||||
friend class dir_node;
|
friend class dir_node;
|
||||||
|
|
||||||
bool need_clone();
|
bool need_skel_upgrade(node_entry *child);
|
||||||
|
|
||||||
// Node properties
|
// Node properties
|
||||||
string _name;
|
string _name;
|
||||||
@ -113,7 +113,11 @@ public:
|
|||||||
typedef map<string_view, node_entry *> map_type;
|
typedef map<string_view, node_entry *> map_type;
|
||||||
typedef map_type::iterator map_iter;
|
typedef map_type::iterator map_iter;
|
||||||
|
|
||||||
~dir_node() override;
|
~dir_node() override {
|
||||||
|
for (auto &it : children)
|
||||||
|
delete it.second;
|
||||||
|
children.clear();
|
||||||
|
}
|
||||||
|
|
||||||
// Return false to indicate need to upgrade to module
|
// Return false to indicate need to upgrade to module
|
||||||
bool collect_files(const char *module, int dfd);
|
bool collect_files(const char *module, int dfd);
|
||||||
@ -191,13 +195,6 @@ protected:
|
|||||||
template<class T>
|
template<class T>
|
||||||
dir_node(const char *name, T *self) : dir_node(name, DT_DIR, self) {}
|
dir_node(const char *name, T *self) : dir_node(name, DT_DIR, self) {}
|
||||||
|
|
||||||
private:
|
|
||||||
// Root node lookup cache
|
|
||||||
root_node *_root;
|
|
||||||
|
|
||||||
// dir nodes host children
|
|
||||||
map_type children;
|
|
||||||
|
|
||||||
template<class T = node_entry>
|
template<class T = node_entry>
|
||||||
T *iter_to_node(const map_iter &it) {
|
T *iter_to_node(const map_iter &it) {
|
||||||
return reinterpret_cast<T*>(it == children.end() ? nullptr : it->second);
|
return reinterpret_cast<T*>(it == children.end() ? nullptr : it->second);
|
||||||
@ -226,6 +223,12 @@ private:
|
|||||||
return node;
|
return node;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dir nodes host children
|
||||||
|
map_type children;
|
||||||
|
|
||||||
|
// Root node lookup cache
|
||||||
|
root_node *_root;
|
||||||
};
|
};
|
||||||
|
|
||||||
class root_node : public dir_node {
|
class root_node : public dir_node {
|
||||||
@ -243,22 +246,6 @@ private:
|
|||||||
friend class module_node;
|
friend class module_node;
|
||||||
};
|
};
|
||||||
|
|
||||||
void node_entry::create_and_mount(const string &src) {
|
|
||||||
const string &dest = node_path();
|
|
||||||
if (is_lnk()) {
|
|
||||||
VLOGI("cp_link", src.data(), dest.data());
|
|
||||||
cp_afc(src.data(), dest.data());
|
|
||||||
} else {
|
|
||||||
if (is_dir())
|
|
||||||
xmkdir(dest.data(), 0);
|
|
||||||
else if (is_reg())
|
|
||||||
close(xopen(dest.data(), O_RDONLY | O_CREAT | O_CLOEXEC, 0));
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
bind_mount(src.data(), dest.data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class module_node : public node_entry {
|
class module_node : public node_entry {
|
||||||
public:
|
public:
|
||||||
module_node(const char *module, dirent *entry)
|
module_node(const char *module, dirent *entry)
|
||||||
@ -270,45 +257,23 @@ public:
|
|||||||
|
|
||||||
module_node(inter_node *node) : module_node(node, node->module) {}
|
module_node(inter_node *node) : module_node(node, node->module) {}
|
||||||
|
|
||||||
void mount() override {
|
void mount() override;
|
||||||
string src = module_mnt + module + parent()->root()->prefix + node_path();
|
|
||||||
if (exist())
|
|
||||||
clone_attr(mirror_path().data(), src.data());
|
|
||||||
if (isa<skel_node>(parent()))
|
|
||||||
create_and_mount(src);
|
|
||||||
else if (is_dir() || is_reg())
|
|
||||||
bind_mount(src.data(), node_path().data());
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const char *module;
|
const char *module;
|
||||||
};
|
};
|
||||||
|
|
||||||
class mirror_node : public dir_node {
|
class mirror_node : public node_entry {
|
||||||
public:
|
public:
|
||||||
mirror_node(dirent *entry) : dir_node(entry->d_name, entry->d_type, this) {}
|
mirror_node(dirent *entry) : node_entry(entry->d_name, entry->d_type, this) {}
|
||||||
|
|
||||||
void mount() override {
|
void mount() override {
|
||||||
create_and_mount(mirror_path());
|
create_and_mount(mirror_path());
|
||||||
dir_node::mount();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class skel_node : public dir_node {
|
class skel_node : public dir_node {
|
||||||
public:
|
public:
|
||||||
skel_node(node_entry *node) : dir_node(node, this) {}
|
skel_node(node_entry *node);
|
||||||
|
void mount() override;
|
||||||
void mount() override {
|
|
||||||
string src = mirror_path();
|
|
||||||
const string &dest = node_path();
|
|
||||||
file_attr a;
|
|
||||||
getattr(src.data(), &a);
|
|
||||||
mkdir(dest.data(), 0);
|
|
||||||
xmount("tmpfs", dest.data(), "tmpfs", 0, nullptr);
|
|
||||||
VLOGI("mnt_tmp", "tmpfs", dest.data());
|
|
||||||
setattr(dest.data(), &a);
|
|
||||||
dir_node::mount();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Poor man's dynamic cast without RTTI
|
// Poor man's dynamic cast without RTTI
|
||||||
@ -347,11 +312,9 @@ const string &node_entry::node_path() {
|
|||||||
return _node_path;
|
return _node_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
dir_node::~dir_node() {
|
/*************************
|
||||||
for (auto &it : children)
|
* Node Tree Construction
|
||||||
delete it.second;
|
*************************/
|
||||||
children.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Func>
|
template<typename Func>
|
||||||
dir_node::map_iter dir_node::insert(dir_node::map_iter it, uint8_t type, Func fn, bool allow_same) {
|
dir_node::map_iter dir_node::insert(dir_node::map_iter it, uint8_t type, Func fn, bool allow_same) {
|
||||||
@ -395,66 +358,71 @@ node_entry* dir_node::extract(string_view name) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool node_entry::need_clone() {
|
skel_node::skel_node(node_entry *node) : dir_node(node, this) {
|
||||||
/* We need to upgrade parent to skeleton if:
|
string mirror = mirror_path();
|
||||||
|
if (auto dir = open_dir(mirror.data()); dir) {
|
||||||
|
set_exist(true);
|
||||||
|
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||||
|
// Insert mirror nodes
|
||||||
|
emplace<mirror_node>(entry->d_name, entry);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// It is actually possible that mirror does not exist (nested mount points)
|
||||||
|
// Set self to non exist so this node will be ignored at mount
|
||||||
|
set_exist(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = children.begin(); it != children.end(); ++it) {
|
||||||
|
// Need to upgrade all inter_node children to skel_node
|
||||||
|
if (isa<inter_node>(it->second))
|
||||||
|
it = upgrade<skel_node>(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool node_entry::need_skel_upgrade(node_entry *child) {
|
||||||
|
/* We need to upgrade to skeleton if:
|
||||||
* - Target does not exist
|
* - Target does not exist
|
||||||
* - Source or target is a symlink */
|
* - Source or target is a symlink */
|
||||||
bool clone = false;
|
bool upgrade = false;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(node_path().data(), &st) != 0) {
|
if (lstat(child->node_path().data(), &st) != 0) {
|
||||||
clone = true;
|
upgrade = true;
|
||||||
} else {
|
} else {
|
||||||
set_exist(); // cache exist result
|
child->set_exist(true);
|
||||||
if (is_lnk() || S_ISLNK(st.st_mode))
|
if (child->is_lnk() || S_ISLNK(st.st_mode))
|
||||||
clone = true;
|
upgrade = true;
|
||||||
}
|
}
|
||||||
return clone;
|
return upgrade;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dir_node::prepare() {
|
bool dir_node::prepare() {
|
||||||
bool upgrade_skel = false;
|
bool to_skel = false;
|
||||||
for (auto it = children.begin(); it != children.end();) {
|
for (auto it = children.begin(); it != children.end();) {
|
||||||
auto child = it->second;
|
if (need_skel_upgrade(it->second)) {
|
||||||
if (child->need_clone()) {
|
|
||||||
if (node_type > type_id<skel_node>()) {
|
if (node_type > type_id<skel_node>()) {
|
||||||
// Upgrade will fail
|
// Upgrade will fail, remove the unsupported child node
|
||||||
goto delete_node;
|
delete it->second;
|
||||||
|
it = children.erase(it);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
// Tell parent to upgrade self to skel
|
// Tell parent to upgrade self to skel
|
||||||
upgrade_skel = true;
|
to_skel = true;
|
||||||
// If child is inter_node, upgrade to module
|
// If child is inter_node, upgrade to module
|
||||||
if (auto nit = upgrade<module_node, inter_node>(it); nit != children.end()) {
|
if (auto nit = upgrade<module_node, inter_node>(it); nit != children.end()) {
|
||||||
it = nit;
|
it = nit;
|
||||||
goto next_node;
|
goto next_node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (auto dn = dyn_cast<dir_node>(child); dn && dn->is_dir() && !dn->prepare()) {
|
if (auto dn = dyn_cast<dir_node>(it->second); dn && dn->is_dir() && !dn->prepare()) {
|
||||||
string mirror = dn->mirror_path();
|
// Upgrade child to skeleton
|
||||||
if (access(mirror.data(), F_OK) != 0) {
|
|
||||||
// It is actually possible that mirror does not exist
|
|
||||||
goto delete_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upgrade child to skeleton (shall always success)
|
|
||||||
it = upgrade<skel_node>(it);
|
it = upgrade<skel_node>(it);
|
||||||
auto skel = iter_to_node<skel_node>(it);
|
|
||||||
auto dir = xopen_dir(mirror.data());
|
|
||||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
|
||||||
// Insert mirror nodes
|
|
||||||
skel->emplace<mirror_node>(entry->d_name, entry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
next_node:
|
next_node:
|
||||||
++it;
|
++it;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
delete_node:
|
|
||||||
// Remove the unsupported child node
|
|
||||||
delete it->second;
|
|
||||||
it = children.erase(it);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
return !upgrade_skel;
|
return !to_skel;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dir_node::collect_files(const char *module, int dfd) {
|
bool dir_node::collect_files(const char *module, int dfd) {
|
||||||
@ -482,6 +450,57 @@ bool dir_node::collect_files(const char *module, int dfd) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/************************
|
||||||
|
* Mount Implementations
|
||||||
|
************************/
|
||||||
|
|
||||||
|
void node_entry::create_and_mount(const string &src) {
|
||||||
|
const string &dest = node_path();
|
||||||
|
if (is_lnk()) {
|
||||||
|
VLOGI("cp_link", src.data(), dest.data());
|
||||||
|
cp_afc(src.data(), dest.data());
|
||||||
|
} else {
|
||||||
|
if (is_dir())
|
||||||
|
xmkdir(dest.data(), 0);
|
||||||
|
else if (is_reg())
|
||||||
|
close(xopen(dest.data(), O_RDONLY | O_CREAT | O_CLOEXEC, 0));
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
bind_mount(src.data(), dest.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void module_node::mount() {
|
||||||
|
string src = module_mnt + module + parent()->root()->prefix + node_path();
|
||||||
|
if (exist())
|
||||||
|
clone_attr(mirror_path().data(), src.data());
|
||||||
|
if (isa<skel_node>(parent()))
|
||||||
|
create_and_mount(src);
|
||||||
|
else if (is_dir() || is_reg())
|
||||||
|
bind_mount(src.data(), node_path().data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void skel_node::mount() {
|
||||||
|
if (!exist())
|
||||||
|
return;
|
||||||
|
string src = mirror_path();
|
||||||
|
const string &dest = node_path();
|
||||||
|
file_attr a;
|
||||||
|
getattr(src.data(), &a);
|
||||||
|
mkdir(dest.data(), 0);
|
||||||
|
if (!isa<skel_node>(parent())) {
|
||||||
|
// We don't need another layer of tmpfs if parent is skel
|
||||||
|
xmount("tmpfs", dest.data(), "tmpfs", 0, nullptr);
|
||||||
|
VLOGI("mnt_tmp", "tmpfs", dest.data());
|
||||||
|
}
|
||||||
|
setattr(dest.data(), &a);
|
||||||
|
dir_node::mount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************
|
||||||
|
* Magisk Stuffs
|
||||||
|
****************/
|
||||||
|
|
||||||
class magisk_node : public node_entry {
|
class magisk_node : public node_entry {
|
||||||
public:
|
public:
|
||||||
magisk_node(const char *name) : node_entry(name, DT_REG, this) {}
|
magisk_node(const char *name) : node_entry(name, DT_REG, this) {}
|
||||||
|
Loading…
Reference in New Issue
Block a user