diff --git a/module/jni/Android.mk b/module/jni/Android.mk index 180ba54..8643b20 100644 --- a/module/jni/Android.mk +++ b/module/jni/Android.mk @@ -3,7 +3,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_MODULE := zygisk -LOCAL_SRC_FILES := mount_parser.cpp unmount.cpp main.cpp +LOCAL_SRC_FILES := mount_parser.cpp mountinfo_parser.cpp unmount.cpp main.cpp LOCAL_STATIC_LIBRARIES := libcxx LOCAL_LDLIBS := -llog include $(BUILD_SHARED_LIBRARY) diff --git a/module/jni/include/mount_parser.hpp b/module/jni/include/mount_parser.hpp index f5e26fb..857f5e5 100644 --- a/module/jni/include/mount_parser.hpp +++ b/module/jni/include/mount_parser.hpp @@ -16,14 +16,10 @@ public: int getPassNumber() const; private: - void parseMountOptions(const std::string &input); - - std::string fsname; - std::string dir; - std::string type; + std::string fsname, dir, type; std::unordered_map opts_map; - int freq; - int passno; + int freq, passno; }; std::vector parseMountsFromPath(const char *path); +std::unordered_map parseMountOptions(const std::string &input); diff --git a/module/jni/include/mountinfo_parser.hpp b/module/jni/include/mountinfo_parser.hpp new file mode 100644 index 0000000..1dac18b --- /dev/null +++ b/module/jni/include/mountinfo_parser.hpp @@ -0,0 +1,32 @@ +#include +#include +#include + +class mountinfo_entry_t +{ +public: + mountinfo_entry_t(int mount_id, int parent_id, int major, int minor, + const std::string &root, const std::string &mount_point, + const std::string &mount_options, const std::string &optional_fields, + const std::string &filesystem_type, const std::string &mount_source, + const std::string &super_options); + + const int &getMountId() const; + const int &getParentId() const; + const int &getMajor() const; + const int &getMinor() const; + const std::string &getRoot() const; + const std::string &getMountPoint() const; + const std::unordered_map &getMountOptions() const; + const std::string &getOptionalFields() const; + const std::string &getFilesystemType() const; + const std::string &getMountSource() const; + const std::unordered_map &getSuperOptions() const; + +private: + int mount_id, parent_id, major, minor; + std::string root, mount_point, optional_fields, filesystem_type, mount_source; + std::unordered_map mount_options, super_options; +}; + +std::vector parseMountinfosFromPath(const char *path); diff --git a/module/jni/mount_parser.cpp b/module/jni/mount_parser.cpp index 0d39728..8392ac9 100644 --- a/module/jni/mount_parser.cpp +++ b/module/jni/mount_parser.cpp @@ -11,55 +11,15 @@ mount_entry_t::mount_entry_t(::mntent *entry) : fsname(entry->mnt_fsname), dir(entry->mnt_dir), type(entry->mnt_type), freq(entry->mnt_freq), passno(entry->mnt_passno) { - parseMountOptions(entry->mnt_opts); + opts_map = parseMountOptions(entry->mnt_opts); } -const std::string &mount_entry_t::getFsName() const -{ - return fsname; -} - -const std::string &mount_entry_t::getMountPoint() const -{ - return dir; -} - -const std::string &mount_entry_t::getType() const -{ - return type; -} - -const std::unordered_map &mount_entry_t::getOptions() const -{ - return opts_map; -} - -int mount_entry_t::getDumpFrequency() const -{ - return freq; -} - -int mount_entry_t::getPassNumber() const -{ - return passno; -} - -void mount_entry_t::parseMountOptions(const std::string &input) -{ - std::istringstream iss(input); - std::string token; - while (std::getline(iss, token, ',')) - { - std::istringstream tokenStream(token); - std::string key, value; - - if (std::getline(tokenStream, key, '=')) - { - std::getline(tokenStream, value); // Put what's left in the stream to value, could be empty - opts_map[key] = value; - } - } -} +const std::string &mount_entry_t::getFsName() const { return fsname; } +const std::string &mount_entry_t::getMountPoint() const { return dir; } +const std::string &mount_entry_t::getType() const { return type; } +const std::unordered_map &mount_entry_t::getOptions() const { return opts_map; } +int mount_entry_t::getDumpFrequency() const { return freq; } +int mount_entry_t::getPassNumber() const { return passno; } std::vector parseMountsFromPath(const char *path) { @@ -81,3 +41,22 @@ std::vector parseMountsFromPath(const char *path) endmntent(file); return result; } + +std::unordered_map parseMountOptions(const std::string &input) +{ + std::unordered_map ret; + std::istringstream iss(input); + std::string token; + while (std::getline(iss, token, ',')) + { + std::istringstream tokenStream(token); + std::string key, value; + + if (std::getline(tokenStream, key, '=')) + { + std::getline(tokenStream, value); // Put what's left in the stream to value, could be empty + ret[key] = value; + } + } + return ret; +} diff --git a/module/jni/mountinfo_parser.cpp b/module/jni/mountinfo_parser.cpp new file mode 100644 index 0000000..e903134 --- /dev/null +++ b/module/jni/mountinfo_parser.cpp @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include + +#include "mountinfo_parser.hpp" +#include "mount_parser.hpp" +#include "logging.hpp" + +mountinfo_entry_t::mountinfo_entry_t(int mount_id, int parent_id, int major, int minor, + const std::string &root, const std::string &mount_point, + const std::string &mount_options, const std::string &optional_fields, + const std::string &filesystem_type, const std::string &mount_source, + const std::string &super_options) + : mount_id(mount_id), parent_id(parent_id), major(major), minor(minor), + root(root), mount_point(mount_point), + optional_fields(optional_fields), filesystem_type(filesystem_type), + mount_source(mount_source) +{ + this->mount_options = parseMountOptions(mount_options); + this->super_options = parseMountOptions(super_options); +} + +const int &mountinfo_entry_t::getMountId() const { return mount_id; } +const int &mountinfo_entry_t::getParentId() const { return parent_id; } +const int &mountinfo_entry_t::getMajor() const { return major; } +const int &mountinfo_entry_t::getMinor() const { return minor; } +const std::string &mountinfo_entry_t::getRoot() const { return root; } +const std::string &mountinfo_entry_t::getMountPoint() const { return mount_point; } +const std::unordered_map &mountinfo_entry_t::getMountOptions() const { return mount_options; } +const std::string &mountinfo_entry_t::getOptionalFields() const { return optional_fields; } +const std::string &mountinfo_entry_t::getFilesystemType() const { return filesystem_type; } +const std::string &mountinfo_entry_t::getMountSource() const { return mount_source; } +const std::unordered_map &mountinfo_entry_t::getSuperOptions() const { return super_options; } + +std::vector parseMountinfosFromPath(const char *path) +{ + std::vector ret; + + std::ifstream ifs(path, std::ifstream::in); + if (!ifs) + { + LOGE("parseMountinfosFromPath could not open file \"%s\"", path); + return ret; + } + + for (std::string line; std::getline(ifs, line);) + { + std::istringstream iss(line); + + int mount_id, parent_id, major, minor; + std::string root, mount_point, mount_options, optional_fields, filesystem_type, mount_source, super_options; + char colon; + + // Read the first 6 fields (major, colon and minor are the same field) + iss >> mount_id >> parent_id >> major >> colon >> minor >> root >> mount_point >> mount_options; + if (iss.fail()) + { + LOGE("parseMountinfosFromPath failed to parse the first 6 fields of line: %s", line.c_str()); + continue; + } + + std::string field; + while (iss >> field && field != "-") + { + optional_fields += " " + field; + } + + if (iss.fail()) + { + LOGE("parseMountinfosFromPath failed to parse the optional fields of line: %s", line.c_str()); + continue; + } + + iss >> filesystem_type >> mount_source >> super_options; + if (iss.fail()) + { + LOGE("parseMountinfosFromPath failed to parse the last 3 fields of line: %s", line.c_str()); + continue; + } + + ret.emplace_back(mountinfo_entry_t(mount_id, parent_id, major, minor, + root, mount_point, mount_options, + optional_fields, filesystem_type, mount_source, + super_options)); + } + + return ret; +} diff --git a/module/jni/unmount.cpp b/module/jni/unmount.cpp index 28d66a4..6f3b016 100644 --- a/module/jni/unmount.cpp +++ b/module/jni/unmount.cpp @@ -7,21 +7,33 @@ #include "zygisk.hpp" #include "logging.hpp" #include "mount_parser.hpp" +#include "mountinfo_parser.hpp" constexpr std::array fsname_list = {"KSU", "APatch", "magisk", "worker"}; -static bool shouldUnmount(const mount_entry_t &info) +static bool shouldUnmount(const mountinfo_entry_t &mount_info) { - const auto &mountPoint = info.getMountPoint(); - const auto &type = info.getType(); - const auto &options = info.getOptions(); + const auto &root = mount_info.getRoot(); + + // Unmount all module bind mounts + if (root.rfind("/adb/modules", 0) == 0) + return true; + + return false; +} + +static bool shouldUnmount(const mount_entry_t &mount) +{ + const auto &mountPoint = mount.getMountPoint(); + const auto &type = mount.getType(); + const auto &options = mount.getOptions(); // Unmount everything mounted to /data/adb if (mountPoint.rfind("/data/adb", 0) == 0) return true; // Unmount all module overlayfs and tmpfs - bool doesFsnameMatch = std::find(fsname_list.begin(), fsname_list.end(), info.getFsName()) != fsname_list.end(); + bool doesFsnameMatch = std::find(fsname_list.begin(), fsname_list.end(), mount.getFsName()) != fsname_list.end(); if ((type == "overlay" || type == "tmpfs") && doesFsnameMatch) return true; @@ -42,23 +54,28 @@ static bool shouldUnmount(const mount_entry_t &info) return true; } - // Unmount the bind mount of default Systemless Hosts module of Magisk - // It should've been an overlay instead but we'll make an exception for this one - // TODO: Maybe we can unmount all binds from userdata to system? - if (mountPoint == "/system/etc/hosts") - return true; - return false; } void do_unmount() { std::vector mountPoints; - for (auto &info : parseMountsFromPath("/proc/self/mounts")) + + // Check mounts first + for (auto &mount : parseMountsFromPath("/proc/self/mounts")) { - if (shouldUnmount(info)) + if (shouldUnmount(mount)) { - mountPoints.push_back(info.getMountPoint()); + mountPoints.push_back(mount.getMountPoint()); + } + } + + // Check mountinfos so that we can find bind mounts as well + for (auto &mount_info : parseMountinfosFromPath("/proc/self/mountinfo")) + { + if (shouldUnmount(mount_info)) + { + mountPoints.push_back(mount_info.getMountPoint()); } }