Cleanup module.cpp

This commit is contained in:
topjohnwu 2021-08-28 10:27:45 -07:00
parent 5d162f81c4
commit 6fe9b69aad

View File

@ -15,11 +15,11 @@ using namespace std;
#define TYPE_MIRROR (1 << 0) /* mount from mirror */ #define TYPE_MIRROR (1 << 0) /* mount from mirror */
#define TYPE_INTER (1 << 1) /* intermediate node */ #define TYPE_INTER (1 << 1) /* intermediate node */
#define TYPE_SKEL (1 << 2) /* replace with tmpfs */ #define TYPE_TMPFS (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_SKEL|TYPE_ROOT) #define TYPE_DIR (TYPE_INTER|TYPE_TMPFS|TYPE_ROOT)
static vector<string> module_list; static vector<string> module_list;
@ -27,11 +27,10 @@ class node_entry;
class dir_node; class dir_node;
class inter_node; class inter_node;
class mirror_node; class mirror_node;
class skel_node; class tmpfs_node;
class module_node; class module_node;
class root_node; class root_node;
static void merge_node(node_entry *a, node_entry *b);
template<class T> static bool isa(node_entry *node); template<class T> static bool isa(node_entry *node);
static int bind_mount(const char *from, const char *to) { static int bind_mount(const char *from, const char *to) {
int ret = xmount(from, to, nullptr, MS_BIND, nullptr); int ret = xmount(from, to, nullptr, MS_BIND, nullptr);
@ -44,7 +43,7 @@ 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<dir_node>() { return TYPE_DIR; }
template<> uint8_t type_id<inter_node>() { return TYPE_INTER; } 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<mirror_node>() { return TYPE_MIRROR; }
template<> uint8_t type_id<skel_node>() { return TYPE_SKEL; } 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<module_node>() { return TYPE_MODULE; }
template<> uint8_t type_id<root_node>() { return TYPE_ROOT; } template<> uint8_t type_id<root_node>() { return TYPE_ROOT; }
@ -52,18 +51,18 @@ class node_entry {
public: public:
virtual ~node_entry() = default; virtual ~node_entry() = default;
// Node info
bool is_dir() { return file_type() == DT_DIR; } bool is_dir() { return file_type() == DT_DIR; }
bool is_lnk() { return file_type() == DT_LNK; } bool is_lnk() { return file_type() == DT_LNK; }
bool is_reg() { return file_type() == DT_REG; } bool is_reg() { return file_type() == DT_REG; }
uint8_t type() { return node_type; } uint8_t type() { return node_type; }
const string &name() { return _name; } const string &name() { return _name; }
// Paths
const string &node_path(); const string &node_path();
string mirror_path() { return mirror_dir + node_path(); } string mirror_path() { return mirror_dir + node_path(); }
// Tree methods
dir_node *parent() { return _parent; } dir_node *parent() { return _parent; }
void merge(node_entry *other);
virtual void mount() = 0; virtual void mount() = 0;
@ -76,20 +75,19 @@ protected:
: _name(name), _file_type(file_type), node_type(type_id<T>()) {} : _name(name), _file_type(file_type), node_type(type_id<T>()) {}
template<class T> template<class T>
explicit node_entry(T*) : node_type(type_id<T>()) {} explicit node_entry(T*) : _file_type(0), node_type(type_id<T>()) {}
void create_and_mount(const string &src); void create_and_mount(const string &src);
/* 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(bool b) { if (b) _file_type |= (1 << 7); else _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 class dir_node; friend class dir_node;
bool need_skel_upgrade(node_entry *child); static bool should_be_tmpfs(node_entry *child);
// Node properties // Node properties
string _name; string _name;
@ -104,10 +102,10 @@ private:
class dir_node : public node_entry { class dir_node : public node_entry {
public: public:
friend void merge_node(node_entry *a, node_entry *b); friend void node_entry::merge(node_entry *other);
typedef map<string_view, node_entry *> map_type; using map_type = map<string_view, node_entry *>;
typedef map_type::iterator map_iter; using iterator = map_type::iterator;
~dir_node() override { ~dir_node() override {
for (auto &it : children) for (auto &it : children)
@ -134,7 +132,7 @@ public:
bool is_empty() { return children.empty(); } bool is_empty() { return children.empty(); }
template<class T> template<class T>
T *child(string_view name) { return iter_to_node<T>(children.find(name)); } T *child(string_view name) { return iterator_to_node<T>(children.find(name)); }
// Lazy val // Lazy val
root_node *root() { root_node *root() {
@ -148,30 +146,28 @@ public:
// Return false if rejected // Return false if rejected
bool insert(node_entry *node) { bool insert(node_entry *node) {
return node auto fn = [=](auto) { return node; };
? iter_to_node(insert(children.find(node->_name), node->node_type, return node && iterator_to_node(insert(node->_name, node->node_type, fn));
[=](auto _) { return node; })) != nullptr
: false;
} }
// Return inserted node or null if rejected // Return inserted node or null if rejected
template<class T, class ...Args> template<class T, class ...Args>
T *emplace(string_view name, Args &&...args) { T *emplace(string_view name, Args &&...args) {
return iter_to_node<T>(insert(children.find(name), type_id<T>(), auto fn = [&](auto) { return new T(std::forward<Args>(args)...); };
[&](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 // Return inserted node, existing node with same rank, or null if rejected
template<class T, class ...Args> template<class T, class ...Args>
T *emplace_or_get(string_view name, Args &&...args) { T *emplace_or_get(string_view name, Args &&...args) {
return iter_to_node<T>(insert(children.find(name), type_id<T>(), auto fn = [&](auto) { return new T(std::forward<Args>(args)...); };
[&](auto _) { return new T(std::forward<Args>(args)...); }, true)); return iterator_to_node<T>(insert(name, type_id<T>(), fn, true));
} }
// Return upgraded node or null if rejected // Return upgraded node or null if rejected
template<class T, class ...Args> template<class T, class ...Args>
T *upgrade(string_view name, Args &...args) { T *upgrade(string_view name, Args &...args) {
return iter_to_node<T>(upgrade<T>(children.find(name), args...)); return iterator_to_node<T>(upgrade<T>(children.find(name), args...));
} }
protected: protected:
@ -183,7 +179,7 @@ protected:
template<class T> template<class T>
dir_node(node_entry *node, T *self) : node_entry(self) { dir_node(node_entry *node, T *self) : node_entry(self) {
merge_node(this, node); merge(node);
if constexpr (std::is_same_v<T, root_node>) if constexpr (std::is_same_v<T, root_node>)
_root = self; _root = self;
} }
@ -192,33 +188,27 @@ protected:
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) {}
template<class T = node_entry> template<class T = node_entry>
T *iter_to_node(const map_iter &it) { T *iterator_to_node(iterator it) {
return reinterpret_cast<T*>(it == children.end() ? nullptr : it->second); return static_cast<T*>(it == children.end() ? nullptr : it->second);
} }
/* fn signature: (node_ent *&) -> node_ent * // Emplace insert a new node, or upgrade if the requested type has a higher rank.
* fn gets reference to the existing node, may be null. // Return iterator to new node or end() if insertion is rejected.
* If fn consumes input, need to set reference to null. // If get_same is true and a node with the same rank exists, it will return that node instead.
* Returns new node or null to reject the insertion. */ // 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> template<typename Func>
map_iter insert(map_iter it, uint8_t type, Func fn, bool allow_same = false); 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> template<class To, class From = node_entry, class ...Args>
map_iter upgrade(map_iter it, Args &&...args) { iterator 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 specific type is selected
if (!isa<From>(ex))
return nullptr;
}
auto node = new To(reinterpret_cast<From *>(ex), std::forward<Args>(args)...);
ex = nullptr;
return node;
});
}
// dir nodes host children // dir nodes host children
map_type children; map_type children;
@ -248,7 +238,7 @@ public:
: node_entry(entry->d_name, entry->d_type, this), module(module) {} : node_entry(entry->d_name, entry->d_type, this), module(module) {}
module_node(node_entry *node, const char *module) : node_entry(this), module(module) { module_node(node_entry *node, const char *module) : node_entry(this), module(module) {
merge_node(this, node); merge(node);
} }
explicit module_node(inter_node *node) : module_node(node, node->module) {} explicit module_node(inter_node *node) : module_node(node, node->module) {}
@ -266,9 +256,9 @@ public:
} }
}; };
class skel_node : public dir_node { class tmpfs_node : public dir_node {
public: public:
explicit skel_node(node_entry *node); explicit tmpfs_node(node_entry *node);
void mount() override; void mount() override;
}; };
@ -279,29 +269,29 @@ static bool isa(node_entry *node) {
} }
template<class T> template<class T>
static T *dyn_cast(node_entry *node) { static T *dyn_cast(node_entry *node) {
return isa<T>(node) ? reinterpret_cast<T*>(node) : nullptr; return isa<T>(node) ? static_cast<T*>(node) : nullptr;
}
// Merge b -> a, b will be deleted
static void merge_node(node_entry *a, node_entry *b) {
a->_name.swap(b->_name);
a->_file_type = b->_file_type;
a->_parent = b->_parent;
// Merge children if both is dir
if (auto aa = dyn_cast<dir_node>(a); aa) {
if (auto bb = dyn_cast<dir_node>(b); bb) {
aa->children.merge(bb->children);
for (auto &pair : aa->children)
pair.second->_parent = aa;
}
}
delete b;
} }
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->children.merge(b->children);
for (auto &pair : a->children)
pair.second->_parent = a;
}
}
delete other;
}
const string &node_entry::node_path() { const string &node_entry::node_path() {
if (_parent && _node_path.empty()) if (_parent && _node_path.empty())
_node_path = _parent->node_path() + '/' + _name; _node_path = _parent->node_path() + '/' + _name;
@ -313,16 +303,16 @@ const string &node_entry::node_path() {
*************************/ *************************/
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::iterator dir_node::insert(iterator it, uint8_t type, const Func &fn, bool get_same) {
node_entry *node = nullptr; node_entry *node = nullptr;
if (it != children.end()) { if (it != children.end()) {
// Upgrade existing node only if higher rank
if (it->second->node_type < type) { if (it->second->node_type < type) {
// Upgrade existing node only if higher precedence
node = fn(it->second); node = fn(it->second);
if (!node) if (!node)
return children.end(); return children.end();
if (it->second) if (it->second)
merge_node(node, it->second); node->merge(it->second);
it = children.erase(it); it = children.erase(it);
// Minor optimization to make insert O(1) by using hint // Minor optimization to make insert O(1) by using hint
if (it == children.begin()) if (it == children.begin())
@ -330,20 +320,36 @@ dir_node::map_iter dir_node::insert(dir_node::map_iter it, uint8_t type, Func fn
else else
it = children.emplace_hint(--it, node->_name, node); it = children.emplace_hint(--it, node->_name, node);
} else { } else {
if (allow_same && it->second->node_type == type) if (get_same && it->second->node_type == type)
return it; return it;
return children.end(); return children.end();
} }
} else { } else {
node = fn(node); node = fn(node);
if (!node) if (!node)
return it; return children.end();
node->_parent = this; node->_parent = this;
it = children.emplace(node->_name, node).first; it = children.emplace(node->_name, node).first;
} }
return it; 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) { node_entry* dir_node::extract(string_view name) {
auto it = children.find(name); auto it = children.find(name);
if (it != children.end()) { if (it != children.end()) {
@ -354,9 +360,9 @@ node_entry* dir_node::extract(string_view name) {
return nullptr; return nullptr;
} }
skel_node::skel_node(node_entry *node) : dir_node(node, this) { tmpfs_node::tmpfs_node(node_entry *node) : dir_node(node, this) {
string mirror = mirror_path(); string mirror = mirror_path();
if (auto dir = open_dir(mirror.data()); dir) { 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()));) {
// Insert mirror nodes // Insert mirror nodes
@ -370,40 +376,39 @@ skel_node::skel_node(node_entry *node) : dir_node(node, this) {
} }
for (auto it = children.begin(); it != children.end(); ++it) { for (auto it = children.begin(); it != children.end(); ++it) {
// Need to upgrade all inter_node children to skel_node // Need to upgrade all inter_node children to tmpfs_node
if (isa<inter_node>(it->second)) if (isa<inter_node>(it->second))
it = upgrade<skel_node>(it); it = upgrade<tmpfs_node>(it);
} }
} }
bool node_entry::need_skel_upgrade(node_entry *child) { // We need to upgrade to tmpfs node if any 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 node_entry::should_be_tmpfs(node_entry *child) {
bool upgrade = false;
struct stat st; struct stat st;
if (lstat(child->node_path().data(), &st) != 0) { if (lstat(child->node_path().data(), &st) != 0) {
upgrade = true; return true;
} else { } else {
child->set_exist(true); child->set_exist(true);
if (child->is_lnk() || S_ISLNK(st.st_mode)) if (child->is_lnk() || S_ISLNK(st.st_mode))
upgrade = true; return true;
} }
return upgrade; return false;
} }
bool dir_node::prepare() { bool dir_node::prepare() {
bool to_skel = false; bool to_tmpfs = false;
for (auto it = children.begin(); it != children.end();) { for (auto it = children.begin(); it != children.end();) {
if (need_skel_upgrade(it->second)) { if (should_be_tmpfs(it->second)) {
if (node_type > type_id<skel_node>()) { if (node_type > type_id<tmpfs_node>()) {
// Upgrade will fail, remove the unsupported child node // Upgrade will fail, remove the unsupported child node
delete it->second; delete it->second;
it = children.erase(it); it = children.erase(it);
continue; continue;
} }
// Tell parent to upgrade self to skel // Tell parent to upgrade self to tmpfs
to_skel = true; to_tmpfs = 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;
@ -411,13 +416,13 @@ bool dir_node::prepare() {
} }
} }
if (auto dn = dyn_cast<dir_node>(it->second); dn && dn->is_dir() && !dn->prepare()) { if (auto dn = dyn_cast<dir_node>(it->second); dn && dn->is_dir() && !dn->prepare()) {
// Upgrade child to skeleton // Upgrade child to tmpfs
it = upgrade<skel_node>(it); it = upgrade<tmpfs_node>(it);
} }
next_node: next_node:
++it; ++it;
} }
return !to_skel; return !to_tmpfs;
} }
bool dir_node::collect_files(const char *module, int dfd) { bool dir_node::collect_files(const char *module, int dfd) {
@ -469,13 +474,13 @@ void module_node::mount() {
string src = module_mnt + module + parent()->root()->prefix + node_path(); string src = module_mnt + module + parent()->root()->prefix + node_path();
if (exist()) if (exist())
clone_attr(mirror_path().data(), src.data()); clone_attr(mirror_path().data(), src.data());
if (isa<skel_node>(parent())) if (isa<tmpfs_node>(parent()))
create_and_mount(src); create_and_mount(src);
else if (is_dir() || is_reg()) else if (is_dir() || is_reg())
bind_mount(src.data(), node_path().data()); bind_mount(src.data(), node_path().data());
} }
void skel_node::mount() { void tmpfs_node::mount() {
if (!exist()) if (!exist())
return; return;
string src = mirror_path(); string src = mirror_path();
@ -483,7 +488,7 @@ void skel_node::mount() {
file_attr a; file_attr a;
getattr(src.data(), &a); getattr(src.data(), &a);
mkdir(dest.data(), 0); mkdir(dest.data(), 0);
if (!isa<skel_node>(parent())) { if (!isa<tmpfs_node>(parent())) {
// We don't need another layer of tmpfs if parent is skel // We don't need another layer of tmpfs if parent is skel
xmount("tmpfs", dest.data(), "tmpfs", 0, nullptr); xmount("tmpfs", dest.data(), "tmpfs", 0, nullptr);
VLOGD("mnt_tmp", "tmpfs", dest.data()); VLOGD("mnt_tmp", "tmpfs", dest.data());