class PropertyManager {
  public:
    explicit PropertyManager(std::string  path) {
	loadFromFile();
}
    std::string getProp(const std::string& key, const std::string& defaultValue = "") {
	auto it = keyIndex.find(key);
	return it != keyIndex.end() ? orderedProps[it->second].second : defaultValue;
}
    void setProp(const std::string& key, const std::string& value) {
	auto it = keyIndex.find(key);
	if (it != keyIndex.end()) {
		orderedProps[it->second].second = value;
	} else {
		size_t idx = orderedProps.size();
		orderedProps.emplace_back(key, value);
		keyIndex[key] = idx;
	}
	saveToFile();
}
  private:
    std::string filePath;
    std::vector<std::pair<std::string, std::string>> orderedProps;
    std::unordered_map<std::string, size_t> keyIndex;
    bool loadFromFile() {
	orderedProps.clear();
	keyIndex.clear();
	std::ifstream file(filePath);
	if (!file.is_open()) {
		LOGW("PropertyManager: Could not open %s for loading.", filePath.c_str());
		return false;
	}
	std::string line;
	while (std::getline(file, line)) {
		if (line.empty() || line[0] == '#')
			continue;
		auto pos = line.find('=');
		if (pos == std::string::npos)
			continue;
		std::string key = line.substr(0, pos);
		std::string val = line.substr(pos + 1);
		size_t idx = orderedProps.size();
		orderedProps.emplace_back(key, val);
		keyIndex[key] = idx;
	}
	file.close();
	return true;
}
    bool saveToFile() {
	std::ofstream file(filePath, std::ios::trunc);
	if (!file.is_open()) {
		LOGE("PropertyManager: Could not open %s for saving.", filePath.c_str());
		return false;
	}
	for (const auto& [key, value] : orderedProps) {
		file << key << "=" << value << '\n';
	}
	file.close();
	return true;
}
}

/* Copyright 2022-2023 John "topjohnwu" Wu
 * Copyright 2024 The NoHello Contributors
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include <cstdlib>
#include <unistd.h>
#include <fcntl.h>
#include <android/log.h>
#include <filesystem>
#include <ranges>
#include <vector>
#include <utility> // For std::pair, std::move

#include "zygisk.hpp"
#include "external/android_filesystem_config.h"
#include "mountsinfo.cpp"
#include "utils.cpp"
#include "external/fdutils/fd_utils.cpp"
#include <sys/mount.h>
#include <sys/ptrace.h>
#include <endian.h>
#include <thread>
#include "log.h"
#include "PropertyManager.cpp"
#include "MountRuleParser.cpp"
#include "external/emoji.h"

using zygisk::Api;
using zygisk::AppSpecializeArgs;
using zygisk::ServerSpecializeArgs;

namespace fs = std::filesystem;

static constexpr off_t EXT_SUPERBLOCK_OFFSET = 0x400;
static constexpr off_t EXT_MAGIC_OFFSET = 0x38;
static constexpr off_t EXT_ERRORS_OFFSET = 0x3C;
static constexpr uint16_t EXT_MAGIC = 0xEF53;
static const std::vector<std::string> defaultRules = {
		R"(source { "KSU", "APatch", "magisk", "worker" } fs { "tmpfs" "overlay" })"
};

enum Advice {
	NORMAL = 0,
	MODULE_CONFLICT = 2,
};

enum State {
	SUCCESS = 0,
	FAILURE = 1
};

static const std::set<std::string> toumount_sources = {"KSU", "APatch", "magisk", "worker"};
static const std::string adbPathPrefix = "/data/adb";

static bool anomaly(MountRootResolver mrs, const MountInfo &mount) {
	const std::string resolved_root = mrs.resolveRoot(mount);
	if (resolved_root.starts_with(adbPathPrefix) || mount.getMountPoint().starts_with(adbPathPrefix)) {
		return true;
	}
	const auto& fs_type = mount.getFsType();
	const auto& mnt_src = mount.getMountSource();
	if (toumount_sources.count(mnt_src)) {
		return true;
	}
	if (fs_type == "overlay") {
		if (toumount_sources.count(mnt_src)) {
			return true;
		}
		const auto& fm = mount.getMountOptions().flagmap;
		if (fm.count("lowerdir") && fm.at("lowerdir").starts_with(adbPathPrefix)) {
			return true;
		}
		if (fm.count("upperdir") && fm.at("upperdir").starts_with(adbPathPrefix)) {
			return true;
		}
		if (fm.count("workdir") && fm.at("workdir").starts_with(adbPathPrefix)) {
			return true;
		}
	} else if (fs_type == "tmpfs") {
		if (toumount_sources.count(mnt_src)) {
			return true;
		}
	}
	return false;
}

static bool anomaly(const MountRuleParser::MountRule& rule, MountRootResolver mrs, const MountInfo &mount) {
	const std::string resolvedRoot = mrs.resolveRoot(mount);
	const auto& fsType = mount.getFsType();
	if (fsType != "overlay") {
		return rule.matches(resolvedRoot, mount.getMountPoint(), fsType, mount.getMountSource());
	} else {
		const auto& fm = mount.getMountOptions().flagmap;
		std::vector<std::string> roots = {resolvedRoot};
		for (const auto* key : {"lowerdir", "upperdir", "workdir"}) {
			auto it = fm.find(key);
			if (it != fm.end()) {
				roots.push_back(it->second);
			}
		}
		return rule.matches(roots, mount.getMountPoint(), fsType, mount.getMountSource());
	}
	return false;
}

static bool anomaly(const std::vector<MountRuleParser::MountRule>& rules, MountRootResolver mrs, const MountInfo &mount) {
	const std::string resolvedRoot = mrs.resolveRoot(mount);
	const auto& fsType = mount.getFsType();
	const auto& fm = mount.getMountOptions().flagmap;
	for (const auto& rule : rules) {
		if (fsType != "overlay") {
			if (rule.matches(resolvedRoot, mount.getMountPoint(), fsType, mount.getMountSource()))
				return true;
		} else {
			std::vector<std::string> roots = {resolvedRoot};
			for (const auto* key : {"lowerdir", "upperdir", "workdir"}) {
				auto it = fm.find(key);
				if (it != fm.end()) {
					roots.push_back(it->second);
				}
			}
			if (rule.matches(roots, mount.getMountPoint(), fsType, mount.getMountSource()))
				return true;
		}
	}
	return false;
}

static std::pair<bool, bool> anomaly(const std::unique_ptr<FileDescriptorInfo> fdi) {
	if (fdi->is_sock) {
		std::string socket_name;
		if (fdi->GetSocketName(&socket_name)) {
			if (socket_name.find("magisk") != std::string::npos ||
				socket_name.find("kernelsu") != std::string::npos || // For KernelSU daemon, common pattern
				socket_name.find("ksud") != std::string::npos || // KernelSU daemon
				socket_name.find("apatchd") != std::string::npos || // For APatch daemon, common pattern
				socket_name.find("apd") != std::string::npos      // APatch daemon
					) {
				LOGD("Marking sensitive socket FD %d (%s) for sanitization.", fdi->fd, socket_name.c_str());
				return {true, true};
			}
		}
	} else { // Not a socket
		if (!fdi->file_path.starts_with("/memfd:") &&
			!fdi->file_path.starts_with("/dev/ashmem") && // Common, usually not root related
			!fdi->file_path.starts_with("[anon_inode:") && // e.g., [anon_inode:sync_fence]
			!fdi->file_path.empty() // Ensure path is not empty
				) {
			if (fdi->file_path.starts_with(adbPathPrefix) ||
				fdi->file_path.find("magisk") != std::string::npos ||
				fdi->file_path.find("kernelsu") != std::string::npos ||
				fdi->file_path.find("apatch") != std::string::npos) {
				LOGD("Marking sensitive file FD %d (%s) for sanitization.", fdi->fd, fdi->file_path.c_str());
				return {true, true};
			}
		}
	}
	return {false, false};
}

static std::unique_ptr<std::string> getExternalErrorBehaviour(const MountInfo& mount) {
	const auto& fs = mount.getFsType();
	if (fs != "ext2" && fs != "ext3" && fs != "ext4")
		return nullptr;
	std::ifstream mntsrc(mount.getMountSource(), std::ios::binary);
	if (!mntsrc || !mntsrc.is_open())
		return nullptr;
	uint16_t magic;
	mntsrc.seekg(EXT_SUPERBLOCK_OFFSET + EXT_MAGIC_OFFSET, std::ios::beg);
	mntsrc.read(reinterpret_cast<char *>(&magic), sizeof(magic));
	if (!mntsrc || mntsrc.gcount() != sizeof(magic))
		return nullptr;
	magic = le16toh(magic);
	if (magic != EXT_MAGIC)
		return nullptr;
	uint16_t errors;
	mntsrc.seekg(EXT_SUPERBLOCK_OFFSET + EXT_ERRORS_OFFSET, std::ios::beg);
	mntsrc.read(reinterpret_cast<char *>(&errors), sizeof(errors));
	if (!mntsrc || mntsrc.gcount() != sizeof(errors))
		return nullptr;
	errors = le16toh(errors);
	switch (errors)
	{
		case 1:
			return std::make_unique<std::string>("continue");
		case 2:
			return std::make_unique<std::string>("remount-ro");
		case 3:
			return std::make_unique<std::string>("panic");
		default:
			return nullptr;
	}
	return nullptr;
}

static void doumount(const std::string& mntPnt);

static void unmount(const std::vector<MountInfo>& mounts) {
	MountRootResolver mrs(mounts);
	for (const auto& mount : std::ranges::reverse_view(mounts)) {
		if (anomaly(mrs, mount))
			doumount(mount.getMountPoint());
	}
}

static void unmount(const std::vector<MountRuleParser::MountRule>& rules, const std::vector<MountInfo>& mounts) {
	MountRootResolver mrs(mounts);
	for (const auto& mount : std::ranges::reverse_view(mounts)) {
		if (anomaly(rules, mrs, mount))
			doumount(mount.getMountPoint());
	}
}

static void unmount(const MountRuleParser::MountRule& rule, const std::vector<MountInfo>& mounts) {
	MountRootResolver mrs(mounts);
	for (const auto& mount : std::ranges::reverse_view(mounts)) {
		if (anomaly(rule, mrs, mount))
			doumount(mount.getMountPoint());
	}
}

static void doumount(const std::string& mntPnt) {
	errno = 0;
	int res;
	const char *mntpnt = mntPnt.c_str();
	if ((res = umount2(mntpnt, MNT_DETACH)) == 0)
		LOGD("umount2(\"%s\", MNT_DETACH): returned (0): 0 (Success)", mntpnt);
	else
		LOGE("umount2(\"%s\", MNT_DETACH): returned %d: %d (%s)", mntpnt, res, errno, strerror(errno));
}

static void remount(const std::vector<MountInfo>& mounts) {
	for (const auto& mount : mounts) {
		if (mount.getMountPoint() == "/data") {
			const auto& mntopts = mount.getMountOptions();
			const auto& fm = mntopts.flagmap;
			if (!fm.count("errors"))
				break;
			auto errors = getExternalErrorBehaviour(mount);
			if (!errors || fm.at("errors") == *errors)
				break;
			auto mntflags = mount.getFlags();
			unsigned int flags = MS_REMOUNT;
			if (mntflags & MountFlags::NOSUID) {
				flags |= MS_NOSUID;
			}
			if (mntflags & MountFlags::NODEV) {
				flags |= MS_NODEV;
			}
			if (mntflags & MountFlags::NOEXEC) {
				flags |= MS_NOEXEC;
			}
			if (mntflags & MountFlags::NOATIME) {
				flags |= MS_NOATIME;
			}
			if (mntflags & MountFlags::NODIRATIME) {
				flags |= MS_NODIRATIME;
			}
			if (mntflags & MountFlags::RELATIME) {
				flags |= MS_RELATIME;
			}
			if (mntflags & MountFlags::NOSYMFOLLOW) {
				flags |= MS_NOSYMFOLLOW;
			}
			int res;
			if ((res = ::mount(nullptr, "/data", nullptr, flags, (std::string("errors=") + *errors).c_str())) == 0)
				LOGD("mount(nullptr, \"/data\", nullptr, 0x%x, \"errors=%s\"): returned 0: 0 (Success)", flags, errors->c_str());
			else
				LOGW("mount(NULL, \"/data\", NULL, 0x%x, \"errors=%s\"): returned %d: %d (%s)", flags, errors->c_str(), res, errno, strerror(errno));
			break;
		}
	}
}

int (*ar_unshare)(int) = nullptr;

static int reshare(int flags) {
    errno = 0;
    return ar_unshare(flags & ~(CLONE_NEWNS | CLONE_NEWCGROUP));
}

class NoHello : public zygisk::ModuleBase {
public:
    void onLoad(Api *_api, JNIEnv *_env) override {
        this->api = _api;
        this->env = _env;
    }

    void preAppSpecialize(AppSpecializeArgs *args) override {
        preSpecialize(args);
    }

	void postAppSpecialize(const AppSpecializeArgs *args) override {
		const char *process = env->GetStringUTFChars(args->nice_name, nullptr);
		postSpecialize(process);
		env->ReleaseStringUTFChars(args->nice_name, process);
	}

    void preServerSpecialize(ServerSpecializeArgs *args) override {
        //preSpecialize("system_server"); // System server usually doesn't need this level of hiding
		api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY);
    }

private:
    Api *api{};
    JNIEnv *env{};
	int cfd{};
	dev_t rundev{};
	ino_t runinode{};
	dev_t cdev{};
	ino_t cinode{};

    void preSpecialize(AppSpecializeArgs *args) {
		unsigned int flags = api->getFlags();
		const bool whitelist = access("/data/adb/nohello/whitelist", F_OK) == 0;
		const bool nodirtyro = access("/data/adb/nohello/no_dirtyro_ar", F_OK) == 0;
		if (flags & zygisk::StateFlag::PROCESS_GRANTED_ROOT) {
			api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY);
			return;
		}
		auto fn = [this](const std::string& lib) {
			auto di = devinoby(lib.c_str());
			if (di) {
				return *di;
			} else {
				LOGW("#[zygisk::?] devino[dl_iterate_phdr]: Failed to get device & inode for %s", lib.c_str());
				LOGI("#[zygisk::?] Fallback to use `/proc/self/maps`");
				return devinobymap(lib);
			}
		};
		if ((whitelist && isuserapp(args->uid)) || flags & zygisk::StateFlag::PROCESS_ON_DENYLIST) {
			pid_t pid = getpid(), ppid = getppid();
			cfd = api->connectCompanion(); // Companion FD
			api->exemptFd(cfd);
			if (write(cfd, &ppid, sizeof(ppid)) != sizeof(ppid)) {
				LOGE("#[zygisk::preSpecialize] write: [-> ppid]: %s", strerror(errno));
				close(cfd);
				api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY);
				return;
			}
			int advice;
			if (read(cfd, &advice, sizeof(advice)) != sizeof(advice)) {
				LOGE("#[zygisk::preSpecialize] read: [<- status]: %s", strerror(errno));
				close(cfd);
				api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY);
				return;
			}
			if (advice == MODULE_CONFLICT) {
				close(cfd);
				api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY);
				return;
			}
			std::tie(cdev, cinode) = fn("libc.so");
			if (!cdev && !cinode) {
				LOGE("#[zygisk::preSpecialize] Failed to get device & inode for libc.so");
				close(cfd);
				api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY);
				return;
			}
			std::tie(rundev, runinode) = fn("libandroid_runtime.so");
			if (!rundev && !runinode) {
				LOGE("#[zygisk::preSpecialize] Failed to get device & inode for libandroid_runtime.so");
				close(cfd);
				api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY);
				return;
			}
			api->pltHookRegister(rundev, runinode, "unshare", (void*) reshare, (void**) &ar_unshare);
			api->pltHookCommit();
			if (!nodirtyro) {
				if (auto res = robaseby(rundev, runinode)) {
					/*
					 * Temporary workaround to fix detection in apps that checks Shared_Clean
					 * if >= 512kb
					 */
					auto [base, size] = *res;
					long pagesz = sysconf(_SC_PAGESIZE);
					size_t alignedSize = (size + pagesz - 1) & ~(pagesz - 1);
					mprotect(base, alignedSize, PROT_READ | PROT_WRITE);
					for (uintptr_t addr = (uintptr_t) base; addr < (uintptr_t) base + size; addr += pagesz) {
						void* page = reinterpret_cast<void*>(addr);
						uint8_t orig = *(volatile uint8_t*) page;
						*(volatile uint8_t*) page = orig;
					}
					mprotect(base, alignedSize, PROT_READ);
					madvise(base, alignedSize, MADV_REMOVE);
				} else {
					LOGW("#[zygisk::preSpecialize] Failed to get ro block for libandroid_runtime.so");
				}
			}
			std::vector<std::pair<std::unique_ptr<FileDescriptorInfo>, bool>> fdSanitizeList; // bool is shouldDetach
			auto fds = GetOpenFds([](const std::string &error){
				LOGE("#[zygisk::preSpecialize] GetOpenFds: %s", error.c_str());
			});
			if (fds) {
				for (auto &fd : *fds) {
					if (fd == cfd) continue; // Skip companion FD itself
					auto fdi = FileDescriptorInfo::CreateFromFd(fd, [fd](const std::string &error){
						LOGE("#[zygisk::preSpecialize] CreateFromFd(%d): %s", fd, error.c_str());
					});
					if (!fdi)
						continue;
					auto [canSanitize, shouldDetach] = anomaly(std::move(fdi));
					if (canSanitize) {
						fdSanitizeList.emplace_back(std::move(fdi), shouldDetach);
					}
				}
			}

			int res = unshare(CLONE_NEWNS | CLONE_NEWCGROUP);
			if (res != 0) {
				LOGE("#[zygisk::preSpecialize] unshare: %s", strerror(errno));
				// There's nothing we can do except returning
				close(cfd);
				return;
			}
			res = mount("rootfs", "/", nullptr, MS_SLAVE | MS_REC, nullptr);
			if (res != 0) {
				LOGE("#[zygisk::preSpecialize] mount(rootfs, \"/\", nullptr, MS_SLAVE | MS_REC, nullptr): returned %d: %d (%s)", res, errno, strerror(errno));
				// There's nothing we can do except returning
				close(cfd);
				return;
			}

			static std::vector<MountRuleParser::MountRule> mountRules;

			if (write(cfd, &pid, sizeof(pid)) != sizeof(pid)) {
				LOGE("#[zygisk::preSpecialize] write: [-> pid]: %s", strerror(errno));
				res = FAILURE; // Fallback to unmount from zygote
			} else if (read(cfd, &res, sizeof(res)) != sizeof(res)) {
				LOGE("#[zygisk::preSpecialize] read: [<- status]: %s", strerror(errno));
				res = FAILURE; // Fallback to unmount from zygote
			} else if (res == FAILURE) {
				LOGW("#[zygisk::preSpecialize]: Companion failed, fallback to unmount in zygote process");
				mountRules = MountRuleParser::parseMultipleRules([this]() {
					auto sizeOfRulesPtr = xread<std::size_t>(cfd);
					if (!sizeOfRulesPtr) {
						LOGE("#[zygisk::preSpecialize] read: [sizeOfRules]: %s", strerror(errno));
						return defaultRules;
					}
					auto sizeOfRules = *sizeOfRulesPtr;
					std::vector<std::string> rules(sizeOfRules, "");
					for (int i = 0; i < sizeOfRules; ++i) {
						auto rule = xread<std::string>(cfd);
						if (!rule) {
							LOGE("#[zygisk::preSpecialize] read: [rule (at index %d)]: %s", i, strerror(errno));
							return defaultRules;
						}
						rules[i] = std::move(*rule);
					}
					return rules;
				}());
			}

			close(cfd);

			if (res == FAILURE) {
				LOGW("#[zygisk::preSpecialize]: Companion failed, fallback to unmount in zygote process");
				unmount(mountRules, getMountInfo()); // Unmount in current (zygote) namespace as fallback
			}

			// Sanitize FDs after companion communication and potential mount changes
			for (auto &[fdi, shouldDetach] : fdSanitizeList) {
				LOGD("#[zygisk::preSpecialize]: Sanitizing FD %d (path: %s, socket: %d), detach: %d",
					 fdi->fd, fdi->file_path.c_str(), fdi->is_sock, shouldDetach);
				fdi->ReopenOrDetach([
											fd = fdi->fd,
											path = fdi->file_path // Capture path by value for lambda
									](const std::string &error){
					LOGE("#[zygisk::preSpecialize]: Sanitize FD %d (%s): %s", fd, path.c_str(), error.c_str());
				}, shouldDetach);
			}
			return;
		}
        api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY);
    }

	void postSpecialize(const char *process) {
        // Unhook PLT hooks
		if (ar_unshare) {
			api->pltHookRegister(rundev, runinode, "unshare", (void*) ar_unshare, nullptr);
            ar_unshare = nullptr; // Clear pointer
        }
		api->pltHookCommit();
		//close(cfd);
		api->setOption(zygisk::Option::DLCLOSE_MODULE_LIBRARY);
	}

};

static void NoRoot(int fd) {
	pid_t pid = -1, ppid = -1;
	static unsigned int successRate = 0;
	static const std::string description = [] {
		std::ifstream f("/data/adb/modules/zygisk_nohello/description");
		// This file exists only after installing/updating the module
		// It should have the default description
		// Since this is static const it's only evaluated once per boot since companion won't exit
		return f ? ([](std::ifstream& s){ std::string l; std::getline(s, l); return l; })(f) : "A Zygisk module to hide root.";
	}();
	static PropertyManager pm("/data/adb/modules/zygisk_nohello/module.prop");

	static const int compatbility = [] {
		if (fs::exists("/data/adb/modules/zygisk_shamiko") && !fs::exists("/data/adb/modules/zygisk_shamiko/disable"))
			return MODULE_CONFLICT;
		if (fs::exists("/data/adb/modules/zygisk-assistant") && !fs::exists("/data/adb/modules/zygisk-assistant/disable"))
			return MODULE_CONFLICT;
		if (fs::exists("/data/adb/modules/treat_wheel") && !fs::exists("/data/adb/modules/treat_wheel/disable"))
			return MODULE_CONFLICT;
		if (fs::exists("/data/adb/modules/susfs4ksu") && !fs::exists("/data/adb/modules/susfs4ksu/disable"))
			return MODULE_CONFLICT;
		return NORMAL;
	}();

	static const bool doesUmountPersists = []() {
		return fs::exists("/data/adb/nohello/umount_persist") || fs::exists("/data/adb/nohello/umount_persists");
	}();

	static std::vector<std::string> stringRules;
	static std::vector<MountRuleParser::MountRule> mountRules;
	static bool evaluateOnlyOnce = false;

	if (!evaluateOnlyOnce) {
		stringRules = []() {
			std::vector<std::string> rules;
			std::ifstream f("/data/adb/nohello/umount");
			if (f && f.is_open()) {
				std::string line;
				while (std::getline(f, line)) {
					if (!line.empty() && line[0] != '#')
						rules.push_back(line);
				}
				f.close();
			} else {
				rules = defaultRules;
				std::ofstream redef("/data/adb/nohello/umount");
				if (redef && redef.is_open()) {
					for (const auto &rule: rules)
						redef << rule << std::endl;
					f.close();
				} else
					LOGE("Unable to create `/data/adb/nohello/umount`");
			}
			return rules;
		}();
		mountRules = MountRuleParser::parseMultipleRules(stringRules);
		if (doesUmountPersists)
			evaluateOnlyOnce = true;
	}

	if (read(fd, &ppid, sizeof(ppid)) != sizeof(ppid)) {
		LOGE("#[ps::Companion] Failed to read PPID: %s", strerror(errno));
		close(fd);
		return;
	}

	static const pid_t clrMsgZygote = [ppid]() -> pid_t {
		if (fs::exists("/data/adb/nohello/no_clr_ptracemsg"))
			// Apply the fix only by user's choice
			return ppid;
		if (getKernelVersion() >= KERNEL_VERSION(6, 1, 0))
			// The issue was fixed in 6.1+
			// https://marc.info/?l=linux-arch&m=164124554311501&w=2
			return ppid;
		// Re-work this to avoid issues with bootloops
		// https://github.com/PerformanC/ReZygisk/issues/171
		if (ptrace(PTRACE_ATTACH, ppid, nullptr, nullptr) == -1) {
			LOGE("#[ps::Companion] ptrace(PTRACE_ATTACH, %d, nullptr, nullptr): %s", ppid,
				 strerror(errno));
			return -1;
		}
		waitpid(ppid, nullptr, 0);
		if (ptrace(PTRACE_SYSCALL, ppid, nullptr, nullptr) == -1) {
			LOGE("#[ps::Companion] ptrace(PTRACE_SYSCALL, %d, nullptr, nullptr): %s", ppid, strerror(errno));
			ptrace(PTRACE_DETACH, ppid, nullptr, nullptr);
			return -1;
		}
		waitpid(ppid, nullptr, 0);
		ptrace(PTRACE_DETACH, ppid, nullptr, nullptr);
		LOGD("#[ps::Companion] Cleared ptrace_message for zygote(%d)", ppid);
		return ppid;
	}();

	int result = compatbility;
	if (result == MODULE_CONFLICT) {
		pm.setProp("description", "[" + emoji::emojize(":warning: ") + "Conflicting modules] " + description);
		if (write(fd, &result, sizeof(result)) != sizeof(result)) {
			LOGE("#[ps::Companion] Failed to write result: %s", strerror(errno));
		}
		close(fd);
		return;
	} else {
		if (write(fd, &result, sizeof(result)) != sizeof(result)) {
			LOGE("#[ps::Companion] Failed to write result: %s", strerror(errno));
			close(fd);
			return;
		}
	}

	if (read(fd, &pid, sizeof(pid)) != sizeof(pid)) {
        LOGE("#[ps::Companion] Failed to read PID: %s", strerror(errno));
		close(fd);
		return;
	}

	result = forkcall(
		[pid]()
		{
			int res = nscg2(pid);
			if (!res) { // switchnsto returns true on success (0 from setns)
				LOGE("#[ps::Companion] Switch namespaces failed for PID %d: %s", pid, strerror(errno));
				return FAILURE;
			}
			auto mounts = getMountInfo();
			unmount(mountRules, mounts);
			remount(mounts);
			return SUCCESS;
		}
	);

	if (result == SUCCESS) {
		successRate++;
		pm.setProp("description", "[" + emoji::emojize(":yum: ") + "Nohello unmounted " +
								  std::to_string(successRate) + " time(s)] " + description);
	} else if (result == FAILURE) {
		if (write(fd, &result, sizeof(result)) != sizeof(result)) {
			LOGE("#[ps::Companion] Failed to write result: %s", strerror(errno));
			close(fd);
			return;
		}
		if (xwrite(fd, stringRules.size())) {
			for (int i = 0; i < stringRules.size(); ++i) {
				if (!xwrite(fd, stringRules[i])) {
					LOGE("#[ps::Companion] write: [rule (at index %d)]: %s", i, strerror(errno));
					close(fd);
					return;
				}
			}
		} else {
			LOGE("#[ps::Companion] write: [stringRules.size()]: %s", strerror(errno));
			close(fd);
			return;
		}
		close(fd);
		return;
	}
	if (write(fd, &result, sizeof(result)) != sizeof(result)) {
		LOGE("#[ps::Companion] Failed to write result: %s", strerror(errno));
		close(fd);
		return;
	}
	close(fd);
}

// Register our module class and the companion handler function
REGISTER_ZYGISK_MODULE(NoHello)
REGISTER_ZYGISK_COMPANION(NoRoot)

#pragma once

#include <string>
#include <unordered_map>
#include <sstream>
#include <fstream>
#include <vector>
#include <cstdint>
#include <type_traits>
#include <algorithm>
#include <sys/types.h>
#include <sys/sysmacros.h>

enum class MountFlags : uint64_t {
	RO         = 1ull << 0,
	RW         = 1ull << 1,
	NOEXEC     = 1ull << 2,
	EXEC       = 1ull << 3,
	NOSUID     = 1ull << 4,
	SUID       = 1ull << 5,
	NODEV      = 1ull << 6,
	DEV        = 1ull << 7,
	RELATIME   = 1ull << 8,
	NORELATIME = 1ull << 9,
	STRICTATIME= 1ull << 10,
	NOATIME    = 1ull << 11,
	ATIME      = 1ull << 12,
	DIRSYNC    = 1ull << 13,
	SYNC       = 1ull << 14,
	MANDLOCK   = 1ull << 15,
	NODIRATIME = 1ull << 16,
	LAZYTIME   = 1ull << 17,
	SECLABEL   = 1ull << 18,
	INDEX      = 1ull << 19,
	NOINDEX    = 1ull << 20,
	METACOPY   = 1ull << 21,
	NOMETACOPY = 1ull << 22,
	BIND       = 1ull << 23,
	RPRIVATE   = 1ull << 24,
	RSHARED    = 1ull << 25,
	RSLAVE     = 1ull << 26,
	PRIVATE    = 1ull << 27,
	SHARED     = 1ull << 28,
	SLAVE      = 1ull << 29,
	REC        = 1ull << 30,
	REMOUNT    = 1ull << 31,
	NOSYMFOLLOW= 1ull << 32
};

inline MountFlags operator|(MountFlags a, MountFlags b) {
	return static_cast<MountFlags>(
			static_cast<std::underlying_type_t<MountFlags>>(a) |
			static_cast<std::underlying_type_t<MountFlags>>(b)
	);
}

inline MountFlags& operator|=(MountFlags& a, MountFlags b) {
	a = a | b;
	return a;
}

inline bool operator&(MountFlags a, MountFlags b) {
	return
			static_cast<std::underlying_type_t<MountFlags>>(a) &
			static_cast<std::underlying_type_t<MountFlags>>(b);
}

enum class PropagationType {
	PRIVATE,
	SHARED,
	SLAVE,
	UNBINDABLE,
	UNKNOWN
};

struct MountPropagation {
	PropagationType type = PropagationType::UNKNOWN;
	int id = -1;
};

static const std::unordered_map<std::string, MountFlags> mountFlags = {
		{"ro", MountFlags::RO}, {"rw", MountFlags::RW}, {"noexec", MountFlags::NOEXEC},
		{"exec", MountFlags::EXEC}, {"nosuid", MountFlags::NOSUID}, {"suid", MountFlags::SUID},
		{"nodev", MountFlags::NODEV}, {"dev", MountFlags::DEV}, {"relatime", MountFlags::RELATIME},
		{"norelatime", MountFlags::NORELATIME}, {"strictatime", MountFlags::STRICTATIME},
		{"noatime", MountFlags::NOATIME}, {"atime", MountFlags::ATIME}, {"dirsync", MountFlags::DIRSYNC},
		{"sync", MountFlags::SYNC}, {"mand", MountFlags::MANDLOCK}, {"nodiratime", MountFlags::NODIRATIME},
		{"lazytime", MountFlags::LAZYTIME}, {"seclabel", MountFlags::SECLABEL}, {"index", MountFlags::INDEX},
		{"noindex", MountFlags::NOINDEX}, {"metacopy", MountFlags::METACOPY},
		{"nometacopy", MountFlags::NOMETACOPY}, {"bind", MountFlags::BIND},
		{"rprivate", MountFlags::RPRIVATE}, {"rshared", MountFlags::RSHARED},
		{"rslave", MountFlags::RSLAVE}, {"private", MountFlags::PRIVATE},
		{"shared", MountFlags::SHARED}, {"slave", MountFlags::SLAVE},
		{"rec", MountFlags::REC}, {"remount", MountFlags::REMOUNT},
		{"nosymfollow", MountFlags::NOSYMFOLLOW}
};

struct MountOptions {
	MountFlags flags = MountFlags(0);
	std::unordered_map<std::string, std::string> flagmap;

	void parse(const std::string& str) {
		std::istringstream s(str);
		std::string opt;
		while (std::getline(s, opt, ',')) {
			auto it = mountFlags.find(opt);
			if (it != mountFlags.end()) {
				flags |= it->second;
			} else {
				size_t eq = opt.find('=');
				if (eq != std::string::npos) {
					flagmap[opt.substr(0, eq)] = opt.substr(eq + 1);
				}
			}
		}
	}
};

class MountInfo {
public:
	explicit MountInfo(const std::string& line) {
		std::istringstream ss(line);
		std::vector<std::string> parts;
		std::string token;
		while (ss >> token)
			parts.push_back(token);
		auto it = std::find(parts.begin(), parts.end(), std::string("-"));
		if (it == parts.end() || std::distance(parts.begin(), it) < 6)
			return;
		size_t sep_idx = std::distance(parts.begin(), it);
		mnt_id = std::stoi(parts[0]);
		mnt_parent_id = std::stoi(parts[1]);
		parseMajorMinor(parts[2]);
		root = parts[3];
		mnt_pnt = parts[4];
		parseFlags(parts[5]);
		for (size_t i = 6; i < sep_idx; ++i)
			parsePropagation(parts[i]);
		fs_type = parts[sep_idx + 1];
		mnt_src = parts[sep_idx + 2];
		parseOptions(parts[sep_idx + 3]);
	}

	~MountInfo() = default;

	[[nodiscard]] int getMountId() const { return mnt_id; }
	[[nodiscard]] int getParentId() const { return mnt_parent_id; }
	[[nodiscard]] dev_t getDev() const { return dev; }
	[[nodiscard]] const std::string& getRoot() const { return root; }
	[[nodiscard]] const std::string& getMountPoint() const { return mnt_pnt; }
	[[nodiscard]] MountFlags getFlags() const { return mnt_flags; }
	[[nodiscard]] const MountPropagation& getPropagation() const { return propagation; }
	[[nodiscard]] const std::string& getFsType() const { return fs_type; }
	[[nodiscard]] const std::string& getMountSource() const { return mnt_src; }
	[[nodiscard]] const MountOptions& getMountOptions() const { return mnt_opts; }

private:
	int mnt_id;
	int mnt_parent_id;
	dev_t dev = 0;
	std::string root;
	std::string mnt_pnt;
	MountFlags mnt_flags = MountFlags(0);
	MountPropagation propagation;
	std::string fs_type;
	std::string mnt_src;
	MountOptions mnt_opts;

	void parseFlags(const std::string& str) {
		std::istringstream s(str);
		std::string opt;
		while (std::getline(s, opt, ',')) {
			auto it = mountFlags.find(opt);
			if (it != mountFlags.end())
				mnt_flags |= it->second;
		}
	}

	void parsePropagation(const std::string& pg) {
		if (pg.find("master:") == 0) {
			propagation.type = PropagationType::SLAVE;
			propagation.id = std::stoi(pg.substr(7));
		} else if (pg.find("shared:") == 0) {
			propagation.type = PropagationType::SHARED;
			propagation.id = std::stoi(pg.substr(7));
		} else if (pg == "unbindable") {
			propagation.type = PropagationType::UNBINDABLE;
		} else if (pg == "private") {
			propagation.type = PropagationType::PRIVATE;
		}
	}

	void parseOptions(const std::string& opt) {
		mnt_opts.parse(opt);
	}

	void parseMajorMinor(const std::string& mmstr) {
		size_t sep = mmstr.find(':');
		if (sep != std::string::npos) {
			int major = std::stoi(mmstr.substr(0, sep));
			int minor = std::stoi(mmstr.substr(sep + 1));
			dev = makedev(major, minor);
		} else {
			dev = 0;
		}
	}
};

class MountRootResolver {
private:
	std::unordered_map<dev_t, std::string> dmm;

public:
	explicit MountRootResolver(const std::vector<MountInfo>& mounts) {
		for (const auto& mount : mounts) {
			if (mount.getRoot() == "/") {
				dmm[mount.getDev()] = mount.getMountPoint();
			}
		}
	}

	~MountRootResolver() = default;

	std::string resolveRoot(const MountInfo& mount) {
		auto dev = mount.getDev();
		auto it = dmm.find(dev);
		if (it != dmm.end()) {
			if (it->second != "/")
				return it->second + mount.getRoot();
		}
		return mount.getRoot();
	}
};

std::vector<MountInfo> getMountInfo(const std::string& path = "/proc/self/mountinfo") {
	std::ifstream mi(path);
	std::vector<MountInfo> mounts;
	std::string line;
	if (!mi.is_open())
		return mounts;
	while (std::getline(mi, line)) {
		MountInfo mountInfo(line);
		mounts.emplace_back(std::move(mountInfo));
	}
	mi.close();
	return mounts;
}

#include <fstream>
#include <sstream>
#include <vector>
#include <unordered_set>
#include <algorithm>
#include <string>
#include "log.h"

class MountRuleParser {
public:
	struct MountRule {
		std::vector<std::string> rootSubstrs;
		std::vector<std::string> mountPointSubstrs;
		std::unordered_set<std::string> fsTypes;
		std::vector<std::string> sources; // changed from unordered_set to vector for wildcard matching

		explicit operator bool() const {
			return !(rootSubstrs.empty() && mountPointSubstrs.empty() && fsTypes.empty() && sources.empty());
		}

		bool matches(const std::string& root, const std::string& mountPoint,
					 const std::string& fsType, const std::string& source) const {
			return matchList(rootSubstrs, root) &&
				   matchList(mountPointSubstrs, mountPoint) &&
				   (fsTypes.empty() || fsTypes.count(fsType) > 0) &&
				   matchSourceList(sources, source);
		}

		bool matches(const std::vector<std::string>& roots, const std::string& mountPoint,
					 const std::string& fsType, const std::string& source) const {
			if (!matchList(mountPointSubstrs, mountPoint) ||
				(!fsTypes.empty() && fsTypes.count(fsType) == 0) ||
				!matchSourceList(sources, source)) {
				return false;
			}
			for (const auto& root : roots) {
				if (matchList(rootSubstrs, root)) {
					return true;
				}
			}
			return false;
		}

	private:
		static bool match_with_wildcard(const std::string& value, const std::string& rawPattern) {
			std::string pattern;
			std::vector<bool> isEscaped;

			for (size_t i = 0; i < rawPattern.size(); ++i) {
				if (rawPattern[i] == '\\' && i + 1 < rawPattern.size()) {
					++i;
					pattern += rawPattern[i];
					isEscaped.push_back(true);
				} else {
					pattern += rawPattern[i];
					isEscaped.push_back(false);
				}
			}

			bool startsWithWildcard = !pattern.empty() && pattern.front() == '*' && !isEscaped[0];
			bool endsWithWildcard = !pattern.empty() &&
									pattern.back() == '*' &&
									!isEscaped[pattern.size() - 1];

			if (startsWithWildcard && endsWithWildcard && pattern.size() > 2) {
				return value.find(pattern.substr(1, pattern.size() - 2)) != std::string::npos;
			} else if (startsWithWildcard) {
				std::string suffix = pattern.substr(1);
				return value.size() >= suffix.size() &&
					   value.compare(value.size() - suffix.size(), suffix.size(), suffix) == 0;
			} else if (endsWithWildcard) {
				std::string prefix = pattern.substr(0, pattern.size() - 1);
				return value.compare(0, prefix.size(), prefix) == 0;
			} else {
				return value == pattern;
			}
		}

		static bool match_source_pattern(const std::string& value, const std::string& rawPattern) {
			std::string pattern;
			std::vector<bool> isEscaped;

			for (size_t i = 0; i < rawPattern.size(); ++i) {
				if (rawPattern[i] == '\\' && i + 1 < rawPattern.size()) {
					++i;
					pattern += rawPattern[i];
					isEscaped.push_back(true);
				} else {
					pattern += rawPattern[i];
					isEscaped.push_back(false);
				}
			}

			bool startsWithWildcard = !pattern.empty() && pattern.front() == '*' && !isEscaped[0];
			bool endsWithWildcard = !pattern.empty() &&
									pattern.back() == '*' &&
									!isEscaped[pattern.size() - 1];

			if (startsWithWildcard && endsWithWildcard && pattern.size() > 2) {
				return value.find(pattern.substr(1, pattern.size() - 2)) != std::string::npos;
			} else if (startsWithWildcard) {
				std::string suffix = pattern.substr(1);
				return value.size() >= suffix.size() &&
					   value.compare(value.size() - suffix.size(), suffix.size(), suffix) == 0;
			} else if (endsWithWildcard) {
				std::string prefix = pattern.substr(0, pattern.size() - 1);
				return value.compare(0, prefix.size(), prefix) == 0;
			} else {
				return value == pattern;
			}
		}

		static bool matchList(const std::vector<std::string>& patterns, const std::string& value) {
			if (patterns.empty()) return true;
			return std::any_of(patterns.begin(), patterns.end(),
							   [&](const std::string& p) { return match_with_wildcard(value, p); });
		}

		static bool matchSourceList(const std::vector<std::string>& patterns, const std::string& value) {
			if (patterns.empty()) return true;
			return std::any_of(patterns.begin(), patterns.end(),
							   [&](const std::string& p) { return match_source_pattern(value, p); });
		}
	};

	// Not necessary anymore
	struct MountEntry {
		std::string root;
		std::string mountPoint;
		std::string fsType;
		std::string mountSource;
	};

	// Used previously for testing its effectiveness
	static MountEntry parseMountinfo(const std::string& line) {
		std::istringstream iss(line);
		std::vector<std::string> tokens;
		std::string token;

		while (iss >> token) tokens.push_back(token);

		auto sep = std::find(tokens.begin(), tokens.end(), "-");
		if (sep == tokens.end() || std::distance(tokens.begin(), sep) < 6) {
			LOGE("[MountRuleParser::parseMultipleRules]: Malformed mountinfo line");
			return {};
		}

		MountEntry entry;
		entry.root = tokens[3];
		entry.mountPoint = tokens[4];
		entry.fsType = *(sep + 1);
		entry.mountSource = *(sep + 2);
		return entry;
	}

	static MountRule parseRuleString(const std::string& ruleText) {
		if (!validateSyntax(ruleText)) {
			return {};
		}

		MountRule rule;
		auto tokens = tokenizePreserveQuotes(ruleText);

		enum Section { NONE, ROOT, POINT, FILESYSTEM, SOURCE } current = NONE;
		enum State { WRITING, IDLE } state = IDLE;

		for (std::string& word : tokens) {
			if (state == IDLE) {
				if (current == NONE) {
					if (word == "root") current = ROOT;
					else if (word == "point") current = POINT;
					else if (word == "fs") current = FILESYSTEM;
					else if (word == "source") current = SOURCE;
				} else if (word == "{") {
					state = WRITING;
				}
			} else if (state == WRITING && word == "}") {
				current = NONE;
				state = IDLE;
			} else {
				if ((word.front() == '"' && word.back() == '"') ||
					(word.front() == '\'' && word.back() == '\'')) {
					word = word.substr(1, word.size() - 2);
				}

				switch (current) {
					case ROOT:
						rule.rootSubstrs.push_back(word);
						break;
					case POINT:
						rule.mountPointSubstrs.push_back(word);
						break;
					case FILESYSTEM:
						rule.fsTypes.insert(word);
						break;
					case SOURCE:
						rule.sources.push_back(word); // changed from insert() to push_back()
						break;
					default:
						break;
				}
			}
		}

		return rule;
	}

	static std::vector<MountRule> parseMultipleRules(const std::vector<std::string>& ruleTexts) {
		std::vector<MountRule> rules;
		for (const auto& text : ruleTexts) {
			MountRule rule = parseRuleString(text);
			if (rule) {
				rules.push_back(rule);
			} else {
				LOGE("[MountRuleParser::parseMultipleRules]: Failed to parse rule: `%s`", text.c_str());
			}
		}
		return rules;
	}

private:
	static bool validateSyntax(const std::string& text) {
		int braceCount = 0;
		bool inDoubleQuotes = false, inSingleQuotes = false;

		for (size_t i = 0; i < text.size(); ++i) {
			char ch = text[i];
			if (ch == '"' && !inSingleQuotes && (i == 0 || text[i - 1] != '\\'))
				inDoubleQuotes = !inDoubleQuotes;
			else if (ch == '\'' && !inDoubleQuotes && (i == 0 || text[i - 1] != '\\'))
				inSingleQuotes = !inSingleQuotes;

			if (!inDoubleQuotes && !inSingleQuotes) {
				if (ch == '{') ++braceCount;
				else if (ch == '}') {
					--braceCount;
					if (braceCount < 0) return false;
				}
			}
		}

		return braceCount == 0 && !inDoubleQuotes && !inSingleQuotes;
	}

	static std::vector<std::string> tokenizePreserveQuotes(const std::string& text) {
		std::vector<std::string> tokens;
		std::string current;
		bool inQuotes = false;
		char quoteChar = '\0';

		for (size_t i = 0; i < text.length(); ++i) {
			char c = text[i];

			if ((c == '"' || c == '\'') && (i == 0 || text[i - 1] != '\\')) {
				if (!inQuotes) {
					inQuotes = true;
					quoteChar = c;
					current += c;
				} else if (c == quoteChar) {
					inQuotes = false;
					current += c;
					tokens.push_back(current);
					current.clear();
				} else {
					current += c;
				}
			} else if (std::isspace(c) && !inQuotes) {
				if (!current.empty()) {
					tokens.push_back(current);
					current.clear();
				}
			} else {
				current += c;
			}
		}

		if (!current.empty()) tokens.push_back(current);
		return tokens;
	}
};

#include <fstream>
#include <sstream>
#include <string>
#include <utility>
#include <iostream>
#include <iomanip>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <vector>
#include <tuple>
#include <cstdint>
#include <sys/mman.h>
#include <link.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <linux/version.h>
#include <dirent.h>
#include "log.h"

std::pair<dev_t, ino_t> devinobymap(const std::string& lib, bool useFind = false, unsigned int *ln = nullptr) {
	std::ifstream maps("/proc/self/maps");
	unsigned int index = 0;
	std::string line;
	std::string needle = "/" + lib;
	while (std::getline(maps, line)) {
		if (ln && index < *ln) {
			index++;
			continue;
		}
		if (line.size() >= needle.size() && ((useFind && line.find(needle) != std::string::npos) ||
			line.compare(line.size() - needle.size(), needle.size(), needle) == 0)) {
			std::istringstream iss(line);
			std::string addr, perms, offset, dev, inode_str;
			iss >> addr >> perms >> offset >> dev >> inode_str;
			std::istringstream devsplit(dev);
			std::string major_hex, minor_hex;
			if (std::getline(devsplit, major_hex, ':') &&
				std::getline(devsplit, minor_hex)) {
				int major = std::stoi(major_hex, nullptr, 16);
				int minor = std::stoi(minor_hex, nullptr, 16);
				dev_t devnum = makedev(major, minor);
				ino_t inode = std::stoul(inode_str);
				if (ln)
					*ln = index;
				return {devnum, inode};
			}
		}
		index++;
	}
	if (ln)
		*ln = -1;
	return {dev_t(0), ino_t(0)};
}

std::optional<std::pair<dev_t, ino_t>> devinoby(const char* lib) {
	struct State {
		const char* needle;
		std::optional<std::pair<dev_t, ino_t>> result;
	} state = { lib };

	dl_iterate_phdr([](struct dl_phdr_info* info, size_t, void* data) -> int {
		auto* s = static_cast<State*>(data);
		if (info->dlpi_name && strstr(info->dlpi_name, s->needle)) {
			struct stat st{};
			if (stat(info->dlpi_name, &st) == 0) {
				s->result = std::make_pair(st.st_dev, st.st_ino);
				return 1; // Stop iteration
			}
		}
		return 0; // Continue
	}, &state);

	return state.result;
}

std::optional<std::pair<void*, size_t>> robaseby(dev_t dev, ino_t ino) {
	struct State {
		dev_t dev;
		ino_t ino;
		std::optional<std::pair<void*, size_t>> result;
	} state = { dev, ino };

	dl_iterate_phdr([](struct dl_phdr_info* info, size_t, void* data) -> int {
		auto* s = static_cast<State*>(data);

		struct stat st{};
		if (stat(info->dlpi_name, &st) != 0)
			return 0;

		if (st.st_dev != s->dev || st.st_ino != s->ino)
			return 0;

		for (int i = 0; i < info->dlpi_phnum; ++i) {
			const auto& phdr = info->dlpi_phdr[i];
			if (phdr.p_type == PT_LOAD &&
				(phdr.p_flags & PF_R) &&
				!(phdr.p_flags & PF_X)) // r--p only
			{
				uintptr_t base = info->dlpi_addr + phdr.p_vaddr;
				size_t size = phdr.p_memsz;
				s->result = std::make_pair(reinterpret_cast<void*>(base), size);
				return 1; // Stop searching
			}
		}

		return 0;
	}, &state);

	return state.result;
}

int forkcall(const std::function<int()> &lambda)
{
	pid_t pid = fork();
	if (pid == -1)
		return -1;
	if (pid == 0) {
		exit(lambda());
	} else {
		int status = -1;
		waitpid(pid, &status, 0);
		if (WIFEXITED(status)) {
			return WEXITSTATUS(status);
		}
	}
	return -1;
}

static inline int seccomp(int op, int fd, void *arg) {
	return syscall(SYS_seccomp, op, fd, arg);
}

static int pidfd_open(pid_t pid, unsigned int flags)
{
	return syscall(SYS_pidfd_open, pid, flags);
}

static ssize_t process_vm_readv(pid_t pid,
								 const struct iovec *local_iov,
								 unsigned long liovcnt,
								 const struct iovec *remote_iov,
								 unsigned long riovcnt,
								 unsigned long flags)
{
	return syscall(SYS_process_vm_readv, pid, local_iov, liovcnt, remote_iov, riovcnt, flags);
}

static ssize_t process_vm_writev(pid_t pid,
								 const struct iovec *local_iov,
								 unsigned long liovcnt,
								 const struct iovec *remote_iov,
								 unsigned long riovcnt,
								 unsigned long flags)
{
	return syscall(SYS_process_vm_writev, pid, local_iov, liovcnt, remote_iov, riovcnt, flags);
}

bool nscg2(pid_t pid) {
    int pidfd = pidfd_open(pid, 0);
    if (pidfd != -1) {
    	// https://man7.org/linux/man-pages/man2/setns.2.html
        int res = setns(pidfd, CLONE_NEWNS | CLONE_NEWCGROUP);
        close(pidfd);
        if (res) {
            LOGE("setns(pidfd_open(%d, 0) -> %d (closed), 0): %s", pid, pidfd, strerror(errno));
            goto fallback;
        }
        return true;
    } else {
        LOGE("pidfd_open(%d): %s", pid, strerror(errno));
    }
fallback:
    {
        std::string mntPath = "/proc/" + std::to_string(pid) + "/ns/mnt";
        int mntfd_fallback = open(mntPath.c_str(), O_RDONLY);
        if (mntfd_fallback != -1) {
            int res = setns(mntfd_fallback, CLONE_NEWNS);
            if (res) {
                LOGE("setns(open(\"%s\") -> %d, CLONE_NEWNS): %s", mntPath.c_str(), mntfd_fallback, strerror(errno));
                close(mntfd_fallback);
                return false;
            }
            close(mntfd_fallback);
        } else {
            LOGE("open(\"%s\"): %s", mntPath.c_str(), strerror(errno));
            return false;
        }
    }
    {
        std::string cgPath = "/proc/" + std::to_string(pid) + "/ns/cgroup";
        int cgfd_fallback = open(cgPath.c_str(), O_RDONLY);
        if (cgfd_fallback != -1) {
            int res = setns(cgfd_fallback, CLONE_NEWCGROUP);
            if (res) {
                LOGE("setns(open(\"%s\") -> %d, CLONE_NEWCGROUP): %s", cgPath.c_str(), cgfd_fallback, strerror(errno));
                close(cgfd_fallback);
                return false;
            }
            close(cgfd_fallback);
        } else {
            LOGE("open(\"%s\"): %s", cgPath.c_str(), strerror(errno));
            return false;
        }
    }
    return true;
}

bool isuserapp(int uid) {
	int appid = uid % AID_USER_OFFSET;
	if (appid >= AID_APP_START && appid <= AID_APP_END)
		return true;
	if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END)
		return true;
	return false;
}

static int sendfd(int sockfd, int fd) {
	int data;
	struct iovec iov{};
	struct msghdr msgh{};
	struct cmsghdr *cmsgp;

	/* Allocate a char array of suitable size to hold the ancillary data.
	   However, since this buffer is in reality a 'struct cmsghdr', use a
	   union to ensure that it is suitably aligned. */
	union {
		char buf[CMSG_SPACE(sizeof(int))];
		/* Space large enough to hold an 'int' */
		struct cmsghdr align;
	} controlMsg{};

	/* The 'msg_name' field can be used to specify the address of the
	   destination socket when sending a datagram. However, we do not
	   need to use this field because 'sockfd' is a connected socket. */

	msgh.msg_name = nullptr;
	msgh.msg_namelen = 0;

	/* On Linux, we must transmit at least one byte of real data in
	   order to send ancillary data. We transmit an arbitrary integer
	   whose value is ignored by recvfd(). */

	msgh.msg_iov = &iov;
	msgh.msg_iovlen = 1;
	iov.iov_base = &data;
	iov.iov_len = sizeof(int);
	data = 12345;

	/* Set 'msghdr' fields that describe ancillary data */

	msgh.msg_control = controlMsg.buf;
	msgh.msg_controllen = sizeof(controlMsg.buf);

	/* Set up ancillary data describing file descriptor to send */

	cmsgp = reinterpret_cast<cmsghdr *>(msgh.msg_control);
	cmsgp->cmsg_level = SOL_SOCKET;
	cmsgp->cmsg_type = SCM_RIGHTS;
	cmsgp->cmsg_len = CMSG_LEN(sizeof(int));
	memcpy(CMSG_DATA(cmsgp), &fd, sizeof(int));

	/* Send real plus ancillary data */

	if (sendmsg(sockfd, &msgh, 0) == -1) return -1;

	return 0;
}

static int recvfd(int sockfd) {
	int data, fd;
	ssize_t nr;
	struct iovec iov{};
	struct msghdr msgh{};

	/* Allocate a char buffer for the ancillary data. See the comments
	   in sendfd() */
	union {
		char buf[CMSG_SPACE(sizeof(int))];
		struct cmsghdr align;
	} controlMsg{};
	struct cmsghdr *cmsgp;

	/* The 'msg_name' field can be used to obtain the address of the
	   sending socket. However, we do not need this information. */

	msgh.msg_name = nullptr;
	msgh.msg_namelen = 0;

	/* Specify buffer for receiving real data */

	msgh.msg_iov = &iov;
	msgh.msg_iovlen = 1;
	iov.iov_base = &data; /* Real data is an 'int' */
	iov.iov_len = sizeof(int);

	/* Set 'msghdr' fields that describe ancillary data */

	msgh.msg_control = controlMsg.buf;
	msgh.msg_controllen = sizeof(controlMsg.buf);

	/* Receive real plus ancillary data; real data is ignored */

	nr = recvmsg(sockfd, &msgh, 0);
	if (nr == -1) return -1;

	cmsgp = CMSG_FIRSTHDR(&msgh);

	/* Check the validity of the 'cmsghdr' */

	if (cmsgp == nullptr || cmsgp->cmsg_len != CMSG_LEN(sizeof(int)) ||
		cmsgp->cmsg_level != SOL_SOCKET || cmsgp->cmsg_type != SCM_RIGHTS) {
		errno = EINVAL;
		return -1;
	}

	/* Return the received file descriptor to our caller */

	memcpy(&fd, CMSG_DATA(cmsgp), sizeof(int));
	return fd;
}

static int getKernelVersion() {
	struct utsname un{};
	if (uname(&un) != 0) {
		return 0;
	}
	int kmaj = 0, kmin = 0, kpatch = 0;
	sscanf(un.release, "%d.%d.%d", &kmaj, &kmin, &kpatch);
	return KERNEL_VERSION(kmaj, kmin, kpatch);
}

template <typename T>
bool xwrite(int fd, const T& data) {
	uint64_t size = sizeof(T);
	if (write(fd, &size, sizeof(size)) != sizeof(size)) {
		return false;
	}
	if (write(fd, data.data(), size) != static_cast<ssize_t>(size)) {
		return false;
	}
	return true;
}

template<>
bool xwrite<std::string>(int fd, const std::string& data) {
	uint64_t size = data.size();
	if (write(fd, &size, sizeof(size)) != sizeof(size)) {
		return false;
	}
	if (write(fd, data.data(), size) != static_cast<ssize_t>(size)) {
		return false;
	}
	return true;
}

bool xwrite(int fd, const char* data) {
	if (!data) return false;
	return xwrite(fd, std::string(data));
}

template <>
bool xwrite<bool>(int fd, const bool& data) {
	uint64_t size = sizeof(bool);
	if (write(fd, &size, sizeof(size)) != sizeof(size)) {
		return false;
	}
	uint8_t byteData = data ? 1 : 0;
	if (write(fd, &byteData, sizeof(byteData)) != sizeof(byteData)) {
		return false;
	}
	return true;
}

template <>
bool xwrite<uintptr_t>(int fd, const uintptr_t& data) {
	uint64_t size = sizeof(uintptr_t);
	if (write(fd, &size, sizeof(size)) != sizeof(size)) {
		return false;
	}
	if (write(fd, &data, size) != size) {
		return false;
	}
	return true;
}

template <typename T>
std::unique_ptr<T> xread(int fd) {
	uint64_t size = 0;
	if (read(fd, &size, sizeof(size)) != sizeof(size)) {
		return nullptr;
	}
	if (size != sizeof(T)) {
		return nullptr;
	}
	auto data = std::make_unique<T>();
	if (read(fd, data.get(), size) != static_cast<ssize_t>(size)) {
		return nullptr;
	}
	return data;
}

template<>
std::unique_ptr<std::string> xread<std::string>(int fd) {
	uint64_t size = 0;
	if (read(fd, &size, sizeof(size)) != sizeof(size)) {
		return nullptr;
	}
	auto data = std::make_unique<std::string>(size, '\0');
	if (read(fd, data->data(), size) != static_cast<ssize_t>(size)) {
		return nullptr;
	}
	return data;
}

template <>
std::unique_ptr<bool> xread<bool>(int fd) {
	uint64_t size = 0;
	if (read(fd, &size, sizeof(size)) != sizeof(size)) {
		return nullptr;
	}
	if (size != sizeof(bool)) {
		return nullptr;
	}
	uint8_t byteData = 0;
	if (read(fd, &byteData, sizeof(byteData)) != sizeof(byteData)) {
		return nullptr;
	}
	return std::make_unique<bool>(byteData != 0);
}

template <>
std::unique_ptr<uintptr_t> xread<uintptr_t>(int fd) {
	uint64_t size = 0;
	if (read(fd, &size, sizeof(size)) != sizeof(size)) {
		return nullptr;
	}
	if (size != sizeof(uintptr_t)) {
		return nullptr;
	}
	uintptr_t data = 0;
	if (read(fd, &data, size) != size) {
		return nullptr;
	}
	return std::make_unique<uintptr_t>(data);
}

#pragma once

#ifndef NOHELLO_LOG_H
#define NOHELLO_LOG_H

#ifdef DEBUG_BUILD
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "NoHello", __VA_ARGS__)
#else
#define LOGD(...) ((void) 0)
#endif

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "NoHello", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "NoHello", __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, "NoHello", __VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, "NoHello", __VA_ARGS__)

#endif //NOHELLO_LOG_H

class
PropertyManager
{
public:
explicit PropertyManager(std::string  path) {}
std::string getProp(const std::string& key, const std::string& defaultValue = "") {}
void setProp(const std::string& key, const std::string& value) {}
private:
std::string filePath;
std::vector<std::pair<std::string, std::string>> orderedProps;
std::unordered_map<std::string, size_t> keyIndex;
bool loadFromFile() {}
bool saveToFile() {}
};

This code segment functions to: Manage persistent key-value storage by loading configuration parameters from a file into an ordered collection, maintaining bidirectional index mapping for efficient access, and synchronizing modifications between memory representation and disk storage while preserving entry order.

```cpp
[TASK_BEGIN]
#include <string>
#include <vector>
#include <unordered_map>
#include <fstream>
#include <utility>

class PropertyManager
{
public:
    explicit PropertyManager(std::string path) : filePath(std::move(path)) {}

    std::string getProp(const std::string& key, const std::string& defaultValue = "") {
        auto it = keyIndex.find(key);
        if (it != keyIndex.end()) {
            return orderedProps[it->second].second;
        }
        return defaultValue;
    }

    void setProp(const std::string& key, const std::string& value) {
        auto it = keyIndex.find(key);
        if (it != keyIndex.end()) {
            orderedProps[it->second].second = value;
        } else {
            orderedProps.emplace_back(key, value);
            keyIndex[key] = orderedProps.size() - 1;
        }
    }

private:
    std::string filePath;
    std::vector<std::pair<std::string, std::string>> orderedProps;
    std::unordered_map<std::string, size_t> keyIndex;

    bool loadFromFile() {
        std::ifstream file(filePath);
        if (!file.is_open()) return false;

        orderedProps.clear();
        keyIndex.clear();

        std::string line;
        while (std::getline(file, line)) {
            size_t eqPos = line.find('=');
            if (eqPos != std::string::npos) {
                std::string key = line.substr(0, eqPos);
                std::string value = line.substr(eqPos + 1);
                orderedProps.emplace_back(key, value);
                keyIndex[key] = orderedProps.size() - 1;
            }
        }
        return true;
    }

    bool saveToFile() {
        std::ofstream file(filePath);
        if (!file.is_open()) return false;

        for (const auto& prop : orderedProps) {
            file << prop.first << "=" << prop.second << "\n";
        }
        return true;
    }
};
[TASK_END]
```


This code segment functions to: Manage persistent configuration settings by loading key-value pairs from a file into memory, enabling value retrieval with default fallback, maintaining insertion order for storage, and automatically synchronizing modifications back to the filesystem.

```cpp
[TASK_BEGIN]
class PropertyManager
{
public:
    explicit PropertyManager(std::string path) : filePath(std::move(path)) {}

    std::string getProp(const std::string& key, const std::string& defaultValue = "") {
        auto it = keyIndex.find(key);
        if (it != keyIndex.end()) {
            return orderedProps[it->second].second;
        }
        return defaultValue;
    }

    void setProp(const std::string& key, const std::string& value) {
        auto it = keyIndex.find(key);
        if (it != keyIndex.end()) {
            orderedProps[it->second].second = value;
        } else {
            orderedProps.emplace_back(key, value);
            keyIndex[key] = orderedProps.size() - 1;
        }
    }

private:
    std::string filePath;
    std::vector<std::pair<std::string, std::string>> orderedProps;
    std::unordered_map<std::string, size_t> keyIndex;

    bool loadFromFile() {
        // Implementation for file loading
        return false;
    }

    bool saveToFile() {
        // Implementation for file saving
        return false;
    }
};
[TASK_END]
```

class PropertyManager
{
public:
    explicit PropertyManager(std::string path) : filePath(std::move(path)) {}
    std::string getProp(const std::string& key, const std::string& defaultValue = "") {
        auto it = keyIndex.find(key);
        if (it != keyIndex.end()) {
            return orderedProps[it->second].second;
        }
        return defaultValue;
    }
    void setProp(const std::string& key, const std::string& value) {
        auto it = keyIndex.find(key);
        if (it != keyIndex.end()) {
            orderedProps[it->second].second = value;
        } else {
            orderedProps.emplace_back(key, value);
            keyIndex[key] = orderedProps.size() - 1;
        }
    }
private:
    std::string filePath;
    std::vector<std::pair<std::string, std::string>> orderedProps;
    std::unordered_map<std::string, size_t> keyIndex;
    bool loadFromFile() {
        return false;
    }
    bool saveToFile() {
        return false;
    }
};


#include <fstream>
#include <sstream>
#include <vector>
#include <unordered_set>
#include <algorithm>
#include <string>
#include "log.h"

class MountRuleParser {
public:
	struct MountRule {
		std::vector<std::string> rootSubstrs;
		std::vector<std::string> mountPointSubstrs;
		std::unordered_set<std::string> fsTypes;
		std::vector<std::string> sources;
		explicit operator bool() const {
			return !(rootSubstrs.empty() && mountPointSubstrs.empty() && fsTypes.empty() && sources.empty());
		}
		bool matches(const std::string& root, const std::string& mountPoint,
					 const std::string& fsType, const std::string& source) const {
			return matchList(rootSubstrs, root) &&
				   matchList(mountPointSubstrs, mountPoint) &&
				   (fsTypes.empty() || fsTypes.count(fsType) > 0) &&
				   matchSourceList(sources, source);
		}
		bool matches(const std::vector<std::string>& roots, const std::string& mountPoint,
					 const std::string& fsType, const std::string& source) const {
			if (!matchList(mountPointSubstrs, mountPoint) ||
				(!fsTypes.empty() && fsTypes.count(fsType) == 0) ||
				!matchSourceList(sources, source)) {
				return false;
			}
			for (const auto& root : roots) {
				if (matchList(rootSubstrs, root)) {
					return true;
				}
			}
			return false;
		}
	private:
		static bool match_with_wildcard(const std::string& value, const std::string& rawPattern) {
			std::string pattern;
			std::vector<bool> isEscaped;
			for (size_t i = 0; i < rawPattern.size(); ++i) {
				if (rawPattern[i] == '\\' && i + 1 < rawPattern.size()) {
					++i;
					pattern += rawPattern[i];
					isEscaped.push_back(true);
				} else {
					pattern += rawPattern[i];
					isEscaped.push_back(false);
				}
			}
			bool startsWithWildcard = !pattern.empty() && pattern.front() == '*' && !isEscaped[0];
			bool endsWithWildcard = !pattern.empty() &&
									pattern.back() == '*' &&
									!isEscaped[pattern.size() - 1];
			if (startsWithWildcard && endsWithWildcard && pattern.size() > 2) {
				return value.find(pattern.substr(1, pattern.size() - 2)) != std::string::npos;
			} else if (startsWithWildcard) {
				std::string suffix = pattern.substr(1);
				return value.size() >= suffix.size() &&
					   value.compare(value.size() - suffix.size(), suffix.size(), suffix) == 0;
			} else if (endsWithWildcard) {
				std::string prefix = pattern.substr(0, pattern.size() - 1);
				return value.compare(0, prefix.size(), prefix) == 0;
			} else {
				return value == pattern;
			}
		}
		static bool match_source_pattern(const std::string& value, const std::string& rawPattern) {
			std::string pattern;
			std::vector<bool> isEscaped;
			for (size_t i = 0; i < rawPattern.size(); ++i) {
				if (rawPattern[i] == '\\' && i + 1 < rawPattern.size()) {
					++i;
					pattern += rawPattern[i];
					isEscaped.push_back(true);
				} else {
					pattern += rawPattern[i];
					isEscaped.push_back(false);
				}
			}
			bool startsWithWildcard = !pattern.empty() && pattern.front() == '*' && !isEscaped[0];
			bool endsWithWildcard = !pattern.empty() &&
									pattern.back() == '*' &&
									!isEscaped[pattern.size() - 1];
			if (startsWithWildcard && endsWithWildcard && pattern.size() > 2) {
				return value.find(pattern.substr(1, pattern.size() - 2)) != std::string::npos;
			} else if (startsWithWildcard) {
				std::string suffix = pattern.substr(1);
				return value.size() >= suffix.size() &&
					   value.compare(value.size() - suffix.size(), suffix.size(), suffix) == 0;
			} else if (endsWithWildcard) {
				std::string prefix = pattern.substr(0, pattern.size() - 1);
				return value.compare(0, prefix.size(), prefix) == 0;
			} else {
				return value == pattern;
			}
		}
		static bool matchList(const std::vector<std::string>& patterns, const std::string& value) {
			if (patterns.empty()) return true;
			return std::any_of(patterns.begin(), patterns.end(),
							   [&](const std::string& p) { return match_with_wildcard(value, p); });
		}
		static bool matchSourceList(const std::vector<std::string>& patterns, const std::string& value) {
			if (patterns.empty()) return true;
			return std::any_of(patterns.begin(), patterns.end(),
							   [&](const std::string& p) { return match_source_pattern(value, p); });
		}
	};
	struct MountEntry {
		std::string root;
		std::string mountPoint;
		std::string fsType;
		std::string mountSource;
	};
	static MountEntry parseMountinfo(const std::string& line) {
		std::istringstream iss(line);
		std::vector<std::string> tokens;
		std::string token;
		while (iss >> token) tokens.push_back(token);
		auto sep = std::find(tokens.begin(), tokens.end(), "-");
		if (sep == tokens.end() || std::distance(tokens.begin(), sep) < 6) {
			LOGE("[MountRuleParser::parseMultipleRules]: Malformed mountinfo line");
			return {};
		}
		MountEntry entry;
		entry.root = tokens[3];
		entry.mountPoint = tokens[4];
		entry.fsType = *(sep + 1);
		entry.mountSource = *(sep + 2);
		return entry;
	}
	static MountRule parseRuleString(const std::string& ruleText) {
		if (!validateSyntax(ruleText)) {
			return {};
		}
		MountRule rule;
		auto tokens = tokenizePreserveQuotes(ruleText);
		enum Section { NONE, ROOT, POINT, FILESYSTEM, SOURCE } current = NONE;
		enum State { WRITING, IDLE } state = IDLE;
		for (std::string& word : tokens) {
			if (state == IDLE) {
				if (current == NONE) {
					if (word == "root") current = ROOT;
					else if (word == "point") current = POINT;
					else if (word == "fs") current = FILESYSTEM;
					else if (word == "source") current = SOURCE;
				} else if (word == "{") {
					state = WRITING;
				}
			} else if (state == WRITING && word == "}") {
				current = NONE;
				state = IDLE;
			} else {
				if ((word.front() == '"' && word.back() == '"') ||
					(word.front() == '\'' && word.back() == '\'')) {
					word = word.substr(1, word.size() - 2);
				}
				switch (current) {
					case ROOT:
						rule.rootSubstrs.push_back(word);
						break;
					case POINT:
						rule.mountPointSubstrs.push_back(word);
						break;
					case FILESYSTEM:
						rule.fsTypes.insert(word);
						break;
					case SOURCE:
						rule.sources.push_back(word);
						break;
					default:
						break;
				}
			}
		}
		return rule;
	}
	static std::vector<MountRule> parseMultipleRules(const std::vector<std::string>& ruleTexts) {
		std::vector<MountRule> rules;
		for (const auto& text : ruleTexts) {
			MountRule rule = parseRuleString(text);
			if (rule) {
				rules.push_back(rule);
			} else {
				LOGE("[MountRuleParser::parseMultipleRules]: Failed to parse rule: `%s`", text.c_str());
			}
		}
		return rules;
	}
private:
	static bool validateSyntax(const std::string& text) {
		int braceCount = 0;
		bool inDoubleQuotes = false, inSingleQuotes = false;
		for (size_t i = 0; i < text.size(); ++i) {
			char ch = text[i];
			if (ch == '"' && !inSingleQuotes && (i == 0 || text[i - 1] != '\\'))
				inDoubleQuotes = !inDoubleQuotes;
			else if (ch == '\'' && !inDoubleQuotes && (i == 0 || text[i - 1] != '\\'))
				inSingleQuotes = !inSingleQuotes;
			if (!inDoubleQuotes && !inSingleQuotes) {
				if (ch == '{') ++braceCount;
				else if (ch == '}') {
					--braceCount;
					if (braceCount < 0) return false;
				}
			}
		}
		return braceCount == 0 && !inDoubleQuotes && !inSingleQuotes;
	}
	static std::vector<std::string> tokenizePreserveQuotes(const std::string& text) {
		std::vector<std::string> tokens;
		std::string current;
		bool inQuotes = false;
		char quoteChar = '\0';
		for (size_t i = 0; i < text.length(); ++i) {
			char c = text[i];
			if ((c == '"' || c == '\'') && (i == 0 || text[i - 1] != '\\')) {
				if (!inQuotes) {
					inQuotes = true;
					quoteChar = c;
					current += c;
				} else if (c == quoteChar) {
					inQuotes = false;
					current += c;
					tokens.push_back(current);
					current.clear();
				} else {
					current += c;
				}
			} else if (std::isspace(c) && !inQuotes) {
				if (!current.empty()) {
					tokens.push_back(current);
					current.clear();
				}
			} else {
				current += c;
			}
		}
		if (!current.empty()) tokens.push_back(current);
		return tokens;
	}
}

class PropertyManager {
  public:
    explicit PropertyManager(std::string  path) {
	loadFromFile();
}
    std::string getProp(const std::string& key, const std::string& defaultValue = "") {
	auto it = keyIndex.find(key);
	return it != keyIndex.end() ? orderedProps[it->second].second : defaultValue;
}
    void setProp(const std::string& key, const std::string& value) {
	auto it = keyIndex.find(key);
	if (it != keyIndex.end()) {
		// Update existing
		orderedProps[it->second].second = value;
	} else {
		// Append new
		size_t idx = orderedProps.size();
		orderedProps.emplace_back(key, value);
		keyIndex[key] = idx;
	}

	saveToFile();
}
  private:
    std::string filePath;
    std::vector<std::pair<std::string, std::string>> orderedProps;
    std::unordered_map<std::string, size_t> keyIndex;
    bool loadFromFile() {
	orderedProps.clear();
	keyIndex.clear();

	std::ifstream file(filePath);
	if (!file.is_open()) {
		LOGW("PropertyManager: Could not open %s for loading.", filePath.c_str());
		return false;
	}

	std::string line;
	while (std::getline(file, line)) {
		if (line.empty() || line[0] == '#')
			continue;

		auto pos = line.find('=');
		if (pos == std::string::npos)
			continue;

		std::string key = line.substr(0, pos);
		std::string val = line.substr(pos + 1);

		size_t idx = orderedProps.size();
		orderedProps.emplace_back(key, val);
		keyIndex[key] = idx;
	}

	file.close();
	return true;
}
    bool saveToFile() {
	std::ofstream file(filePath, std::ios::trunc);
	if (!file.is_open()) {
		LOGE("PropertyManager: Could not open %s for saving.", filePath.c_str());
		return false;
	}

	for (const auto& [key, value] : orderedProps) {
		file << key << "=" << value << '\n';
	}

	file.close();
	return true;
}
};

class
MountRuleParser
{
public:
struct MountRule {
		std::vector<std::string> rootSubstrs;
		std::vector<std::string> mountPointSubstrs;
		std::unordered_set<std::string> fsTypes;
		std::vector<std::string> sources; // changed from unordered_set to vector for wildcard matching

		explicit operator bool() const {
			return !(rootSubstrs.empty() && mountPointSubstrs.empty() && fsTypes.empty() && sources.empty());
		}

		bool matches(const std::string& root, const std::string& mountPoint,
					 const std::string& fsType, const std::string& source) const {
			return matchList(rootSubstrs, root) &&
				   matchList(mountPointSubstrs, mountPoint) &&
				   (fsTypes.empty() || fsTypes.count(fsType) > 0) &&
				   matchSourceList(sources, source);
		}

		bool matches(const std::vector<std::string>& roots, const std::string& mountPoint,
					 const std::string& fsType, const std::string& source) const {
			if (!matchList(mountPointSubstrs, mountPoint) ||
				(!fsTypes.empty() && fsTypes.count(fsType) == 0) ||
				!matchSourceList(sources, source)) {
				return false;
			}
			for (const auto& root : roots) {
				if (matchList(rootSubstrs, root)) {
					return true;
				}
			}
			return false;
		}

	private:
		static bool match_with_wildcard(const std::string& value, const std::string& rawPattern) {
			std::string pattern;
			std::vector<bool> isEscaped;

			for (size_t i = 0; i < rawPattern.size(); ++i) {
				if (rawPattern[i] == '\\' && i + 1 < rawPattern.size()) {
					++i;
					pattern += rawPattern[i];
					isEscaped.push_back(true);
				} else {
					pattern += rawPattern[i];
					isEscaped.push_back(false);
				}
			}

			bool startsWithWildcard = !pattern.empty() && pattern.front() == '*' && !isEscaped[0];
			bool endsWithWildcard = !pattern.empty() &&
									pattern.back() == '*' &&
									!isEscaped[pattern.size() - 1];

			if (startsWithWildcard && endsWithWildcard && pattern.size() > 2) {
				return value.find(pattern.substr(1, pattern.size() - 2)) != std::string::npos;
			} else if (startsWithWildcard) {
				std::string suffix = pattern.substr(1);
				return value.size() >= suffix.size() &&
					   value.compare(value.size() - suffix.size(), suffix.size(), suffix) == 0;
			} else if (endsWithWildcard) {
				std::string prefix = pattern.substr(0, pattern.size() - 1);
				return value.compare(0, prefix.size(), prefix) == 0;
			} else {
				return value == pattern;
			}
		}

		static bool match_source_pattern(const std::string& value, const std::string& rawPattern) {
			std::string pattern;
			std::vector<bool> isEscaped;

			for (size_t i = 0; i < rawPattern.size(); ++i) {
				if (rawPattern[i] == '\\' && i + 1 < rawPattern.size()) {
					++i;
					pattern += rawPattern[i];
					isEscaped.push_back(true);
				} else {
					pattern += rawPattern[i];
					isEscaped.push_back(false);
				}
			}

			bool startsWithWildcard = !pattern.empty() && pattern.front() == '*' && !isEscaped[0];
			bool endsWithWildcard = !pattern.empty() &&
									pattern.back() == '*' &&
									!isEscaped[pattern.size() - 1];

			if (startsWithWildcard && endsWithWildcard && pattern.size() > 2) {
				return value.find(pattern.substr(1, pattern.size() - 2)) != std::string::npos;
			} else if (startsWithWildcard) {
				std::string suffix = pattern.substr(1);
				return value.size() >= suffix.size() &&
					   value.compare(value.size() - suffix.size(), suffix.size(), suffix) == 0;
			} else if (endsWithWildcard) {
				std::string prefix = pattern.substr(0, pattern.size() - 1);
				return value.compare(0, prefix.size(), prefix) == 0;
			} else {
				return value == pattern;
			}
		}

		static bool matchList(const std::vector<std::string>& patterns, const std::string& value) {
			if (patterns.empty()) return true;
			return std::any_of(patterns.begin(), patterns.end(),
							   [&](const std::string& p) { return match_with_wildcard(value, p); });
		}

		static bool matchSourceList(const std::vector<std::string>& patterns, const std::string& value) {
			if (patterns.empty()) return true;
			return std::any_of(patterns.begin(), patterns.end(),
							   [&](const std::string& p) { return match_source_pattern(value, p); });
		}
	};
struct MountEntry {
		std::string root;
		std::string mountPoint;
		std::string fsType;
		std::string mountSource;
	};
static MountEntry parseMountinfo(const std::string& line) {}
static MountRule parseRuleString(const std::string& ruleText) {}
static std::vector<MountRule> parseMultipleRules(const std::vector<std::string>& ruleTexts) {}
private:
static bool validateSyntax(const std::string& text) {}
static std::vector<std::string> tokenizePreserveQuotes(const std::string& text) {}
};

This code segment functions to: Parse structured configuration rules for system resource allocation and perform pattern-based matching on hierarchical data entries using wildcard-enabled criteria while maintaining syntax validation during rule processing.

```cpp
[TASK_BEGIN]
static MountEntry parseMountinfo(const std::string& line) {
    MountEntry entry;
    std::vector<std::string> tokens;
    std::istringstream iss(line);
    std::string token;

    while (iss >> token) {
        tokens.push_back(token);
    }

    auto hyphenIt = std::find(tokens.begin(), tokens.end(), "-");
    if (hyphenIt == tokens.end() || tokens.size() < 10) {
        return entry;
    }

    entry.root = tokens[3];
    entry.mountPoint = tokens[4];
    entry.fsType = *(hyphenIt + 1);
    entry.mountSource = *(hyphenIt + 2);

    return entry;
}

static MountRule parseRuleString(const std::string& ruleText) {
    MountRule rule;
    if (!validateSyntax(ruleText)) {
        LOG_ERROR("Invalid rule syntax: " << ruleText);
        return rule;
    }

    auto parts = tokenizePreserveQuotes(ruleText);
    for (const auto& part : parts) {
        size_t eqPos = part.find('=');
        if (eqPos == std::string::npos) continue;

        std::string key = part.substr(0, eqPos);
        std::string value = part.substr(eqPos + 1);

        auto splitValues = [](const std::string& val) {
            std::vector<std::string> res;
            std::istringstream iss(val);
            std::string item;
            while (std::getline(iss, item, ',')) {
                item.erase(0, item.find_first_not_of(" \t"));
                item.erase(item.find_last_not_of(" \t") + 1);
                if (!item.empty()) res.push_back(item);
            }
            return res;
        };

        if (key == "root") {
            auto substrs = splitValues(value);
            rule.rootSubstrs.insert(rule.rootSubstrs.end(), substrs.begin(), substrs.end());
        } else if (key == "mountpoint") {
            auto substrs = splitValues(value);
            rule.mountPointSubstrs.insert(rule.mountPointSubstrs.end(), substrs.begin(), substrs.end());
        } else if (key == "fstype") {
            auto types = splitValues(value);
            rule.fsTypes.insert(types.begin(), types.end());
        } else if (key == "source") {
            auto sources = splitValues(value);
            rule.sources.insert(rule.sources.end(), sources.begin(), sources.end());
        }
    }
    return rule;
}

static std::vector<MountRule> parseMultipleRules(const std::vector<std::string>& ruleTexts) {
    std::vector<MountRule> rules;
    for (const auto& text : ruleTexts) {
        MountRule rule = parseRuleString(text);
        if (rule) {
            rules.push_back(rule);
        }
    }
    return rules;
}

private:
static bool validateSyntax(const std::string& text) {
    if (text.empty()) return false;

    auto parts = tokenizePreserveQuotes(text);
    for (const auto& part : parts) {
        size_t eqPos = part.find('=');
        if (eqPos == std::string::npos || eqPos == 0 || eqPos == part.length() - 1) return false;

        std::string key = part.substr(0, eqPos);
        if (key != "root" && key != "mountpoint" && key != "fstype" && key != "source") return false;
    }
    return true;
}

static std::vector<std::string> tokenizePreserveQuotes(const std::string& text) {
    std::vector<std::string> tokens;
    std::string current;
    bool inQuotes = false;
    bool escape = false;

    for (char c : text) {
        if (escape) {
            current += c;
            escape = false;
        } else if (c == '\\') {
            escape = true;
        } else if (c == '"') {
            inQuotes = !inQuotes;
        } else if (c == ',' && !inQuotes) {
            if (!current.empty()) {
                tokens.push_back(current);
                current.clear();
            }
        } else {
            current += c;
        }
    }

    if (!current.empty()) tokens.push_back(current);

    // Trim whitespace
    for (auto& token : tokens) {
        size_t first = token.find_first_not_of(" \t");
        if (first == std::string::npos) {
            token.clear();
            continue;
        }
        size_t last = token.find_last_not_of(" \t");
        token = token.substr(first, last - first + 1);
    }

    // Remove empty tokens
    tokens.erase(std::remove_if(tokens.begin(), tokens.end(),
        [](const std::string& s) { return s.empty(); }), tokens.end());

    return tokens;
}
[TASK_END]
```

static MountEntry parseMountinfo(const std::string& line) {
    MountEntry entry;
    std::vector<std::string> tokens;
    std::istringstream iss(line);
    std::string token;
    while (iss >> token) {
        tokens.push_back(token);
    }
    auto hyphenIt = std::find(tokens.begin(), tokens.end(), "-");
    if (hyphenIt == tokens.end() || tokens.size() < 10) {
        return entry;
    }
    entry.root = tokens[3];
    entry.mountPoint = tokens[4];
    entry.fsType = *(hyphenIt + 1);
    entry.mountSource = *(hyphenIt + 2);
    return entry;
}
static MountRule parseRuleString(const std::string& ruleText) {
    MountRule rule;
    if (!validateSyntax(ruleText)) {
        LOG_ERROR("Invalid rule syntax: " << ruleText);
        return rule;
    }
    auto parts = tokenizePreserveQuotes(ruleText);
    for (const auto& part : parts) {
        size_t eqPos = part.find('=');
        if (eqPos == std::string::npos) continue;
        std::string key = part.substr(0, eqPos);
        std::string value = part.substr(eqPos + 1);
        auto splitValues = [](const std::string& val) {
            std::vector<std::string> res;
            std::istringstream iss(val);
            std::string item;
            while (std::getline(iss, item, ',')) {
                item.erase(0, item.find_first_not_of(" \t"));
                item.erase(item.find_last_not_of(" \t") + 1);
                if (!item.empty()) res.push_back(item);
            }
            return res;
        };
        if (key == "root") {
            auto substrs = splitValues(value);
            rule.rootSubstrs.insert(rule.rootSubstrs.end(), substrs.begin(), substrs.end());
        } else if (key == "mountpoint") {
            auto substrs = splitValues(value);
            rule.mountPointSubstrs.insert(rule.mountPointSubstrs.end(), substrs.begin(), substrs.end());
        } else if (key == "fstype") {
            auto types = splitValues(value);
            rule.fsTypes.insert(types.begin(), types.end());
        } else if (key == "source") {
            auto sources = splitValues(value);
            rule.sources.insert(rule.sources.end(), sources.begin(), sources.end());
        }
    }
    return rule;
}
static std::vector<MountRule> parseMultipleRules(const std::vector<std::string>& ruleTexts) {
    std::vector<MountRule> rules;
    for (const auto& text : ruleTexts) {
        MountRule rule = parseRuleString(text);
        if (rule) {
            rules.push_back(rule);
        }
    }
    return rules;
}
private:
static bool validateSyntax(const std::string& text) {
    if (text.empty()) return false;
    auto parts = tokenizePreserveQuotes(text);
    for (const auto& part : parts) {
        size_t eqPos = part.find('=');
        if (eqPos == std::string::npos || eqPos == 0 || eqPos == part.length() - 1) return false;
        std::string key = part.substr(0, eqPos);
        if (key != "root" && key != "mountpoint" && key != "fstype" && key != "source") return false;
    }
    return true;
}
static std::vector<std::string> tokenizePreserveQuotes(const std::string& text) {
    std::vector<std::string> tokens;
    std::string current;
    bool inQuotes = false;
    bool escape = false;
    for (char c : text) {
        if (escape) {
            current += c;
            escape = false;
        } else if (c == '\\') {
            escape = true;
        } else if (c == '"') {
            inQuotes = !inQuotes;
        } else if (c == ',' && !inQuotes) {
            if (!current.empty()) {
                tokens.push_back(current);
                current.clear();
            }
        } else {
            current += c;
        }
    }
    if (!current.empty()) tokens.push_back(current);
    for (auto& token : tokens) {
        size_t first = token.find_first_not_of(" \t");
        if (first == std::string::npos) {
            token.clear();
            continue;
        }
        size_t last = token.find_last_not_of(" \t");
        token = token.substr(first, last - first + 1);
    }
    tokens.erase(std::remove_if(tokens.begin(), tokens.end(),
        [](const std::string& s) { return s.empty(); }), tokens.end());
    return tokens;
}


#pragma once

#include <string>
#include <unordered_map>
#include <sstream>
#include <fstream>
#include <vector>
#include <cstdint>
#include <type_traits>
#include <algorithm>
#include <sys/types.h>
#include <sys/sysmacros.h>

enum class MountFlags : uint64_t {
	RO         = 1ull << 0,
	RW         = 1ull << 1,
	NOEXEC     = 1ull << 2,
	EXEC       = 1ull << 3,
	NOSUID     = 1ull << 4,
	SUID       = 1ull << 5,
	NODEV      = 1ull << 6,
	DEV        = 1ull << 7,
	RELATIME   = 1ull << 8,
	NORELATIME = 1ull << 9,
	STRICTATIME= 1ull << 10,
	NOATIME    = 1ull << 11,
	ATIME      = 1ull << 12,
	DIRSYNC    = 1ull << 13,
	SYNC       = 1ull << 14,
	MANDLOCK   = 1ull << 15,
	NODIRATIME = 1ull << 16,
	LAZYTIME   = 1ull << 17,
	SECLABEL   = 1ull << 18,
	INDEX      = 1ull << 19,
	NOINDEX    = 1ull << 20,
	METACOPY   = 1ull << 21,
	NOMETACOPY = 1ull << 22,
	BIND       = 1ull << 23,
	RPRIVATE   = 1ull << 24,
	RSHARED    = 1ull << 25,
	RSLAVE     = 1ull << 26,
	PRIVATE    = 1ull << 27,
	SHARED     = 1ull << 28,
	SLAVE      = 1ull << 29,
	REC        = 1ull << 30,
	REMOUNT    = 1ull << 31,
	NOSYMFOLLOW= 1ull << 32
};

inline MountFlags operator|(MountFlags a, MountFlags b) {
	return static_cast<MountFlags>(
			static_cast<std::underlying_type_t<MountFlags>>(a) |
			static_cast<std::underlying_type_t<MountFlags>>(b)
	);
}

inline MountFlags& operator|=(MountFlags& a, MountFlags b) {
	a = a | b;
	return a;
}

inline bool operator&(MountFlags a, MountFlags b) {
	return
			static_cast<std::underlying_type_t<MountFlags>>(a) &
			static_cast<std::underlying_type_t<MountFlags>>(b);
}

enum class PropagationType {
	PRIVATE,
	SHARED,
	SLAVE,
	UNBINDABLE,
	UNKNOWN
};

struct MountPropagation {
	PropagationType type = PropagationType::UNKNOWN;
	int id = -1;
};

static const std::unordered_map<std::string, MountFlags> mountFlags = {
		{"ro", MountFlags::RO}, {"rw", MountFlags::RW}, {"noexec", MountFlags::NOEXEC},
		{"exec", MountFlags::EXEC}, {"nosuid", MountFlags::NOSUID}, {"suid", MountFlags::SUID},
		{"nodev", MountFlags::NODEV}, {"dev", MountFlags::DEV}, {"relatime", MountFlags::RELATIME},
		{"norelatime", MountFlags::NORELATIME}, {"strictatime", MountFlags::STRICTATIME},
		{"noatime", MountFlags::NOATIME}, {"atime", MountFlags::ATIME}, {"dirsync", MountFlags::DIRSYNC},
		{"sync", MountFlags::SYNC}, {"mand", MountFlags::MANDLOCK}, {"nodiratime", MountFlags::NODIRATIME},
		{"lazytime", MountFlags::LAZYTIME}, {"seclabel", MountFlags::SECLABEL}, {"index", MountFlags::INDEX},
		{"noindex", MountFlags::NOINDEX}, {"metacopy", MountFlags::METACOPY},
		{"nometacopy", MountFlags::NOMETACOPY}, {"bind", MountFlags::BIND},
		{"rprivate", MountFlags::RPRIVATE}, {"rshared", MountFlags::RSHARED},
		{"rslave", MountFlags::RSLAVE}, {"private", MountFlags::PRIVATE},
		{"shared", MountFlags::SHARED}, {"slave", MountFlags::SLAVE},
		{"rec", MountFlags::REC}, {"remount", MountFlags::REMOUNT},
		{"nosymfollow", MountFlags::NOSYMFOLLOW}
};

struct MountOptions {
	MountFlags flags = MountFlags(0);
	std::unordered_map<std::string, std::string> flagmap;

	void parse(const std::string& str) {
		std::istringstream s(str);
		std::string opt;
		while (std::getline(s, opt, ',')) {
			auto it = mountFlags.find(opt);
			if (it != mountFlags.end()) {
				flags |= it->second;
			} else {
				size_t eq = opt.find('=');
				if (eq != std::string::npos) {
					flagmap[opt.substr(0, eq)] = opt.substr(eq + 1);
				}
			}
		}
	}
};

;

class MountRootResolver {
private:
	std::unordered_map<dev_t, std::string> dmm;

public:
	explicit MountRootResolver(const std::vector<MountInfo>& mounts) {
		for (const auto& mount : mounts) {
			if (mount.getRoot() == "/") {
				dmm[mount.getDev()] = mount.getMountPoint();
			}
		}
	}

	~MountRootResolver() = default;

	std::string resolveRoot(const MountInfo& mount) {
		auto dev = mount.getDev();
		auto it = dmm.find(dev);
		if (it != dmm.end()) {
			if (it->second != "/")
				return it->second + mount.getRoot();
		}
		return mount.getRoot();
	}
};

std::vector<MountInfo> getMountInfo(const std::string& path = "/proc/self/mountinfo") {
	std::ifstream mi(path);
	std::vector<MountInfo> mounts;
	std::string line;
	if (!mi.is_open())
		return mounts;
	while (std::getline(mi, line)) {
		MountInfo mountInfo(line);
		mounts.emplace_back(std::move(mountInfo));
	}
	mi.close();
	return mounts;
}

class MountInfo {
public:
	explicit MountInfo(const std::string& line) {
		std::istringstream ss(line);
		std::vector<std::string> parts;
		std::string token;
		while (ss >> token)
			parts.push_back(token);
		auto it = std::find(parts.begin(), parts.end(), std::string("-"));
		if (it == parts.end() || std::distance(parts.begin(), it) < 6)
			return;
		size_t sep_idx = std::distance(parts.begin(), it);
		mnt_id = std::stoi(parts[0]);
		mnt_parent_id = std::stoi(parts[1]);
		parseMajorMinor(parts[2]);
		root = parts[3];
		mnt_pnt = parts[4];
		parseFlags(parts[5]);
		for (size_t i = 6; i < sep_idx; ++i)
			parsePropagation(parts[i]);
		fs_type = parts[sep_idx + 1];
		mnt_src = parts[sep_idx + 2];
		parseOptions(parts[sep_idx + 3]);
	}
	~MountInfo() = default;
	[[nodiscard]] int getMountId() const { return mnt_id; }
	[[nodiscard]] int getParentId() const { return mnt_parent_id; }
	[[nodiscard]] dev_t getDev() const { return dev; }
	[[nodiscard]] const std::string& getRoot() const { return root; }
	[[nodiscard]] const std::string& getMountPoint() const { return mnt_pnt; }
	[[nodiscard]] MountFlags getFlags() const { return mnt_flags; }
	[[nodiscard]] const MountPropagation& getPropagation() const { return propagation; }
	[[nodiscard]] const std::string& getFsType() const { return fs_type; }
	[[nodiscard]] const std::string& getMountSource() const { return mnt_src; }
	[[nodiscard]] const MountOptions& getMountOptions() const { return mnt_opts; }
private:
	int mnt_id;
	int mnt_parent_id;
	dev_t dev = 0;
	std::string root;
	std::string mnt_pnt;
	MountFlags mnt_flags = MountFlags(0);
	MountPropagation propagation;
	std::string fs_type;
	std::string mnt_src;
	MountOptions mnt_opts;
	void parseFlags(const std::string& str) {
		std::istringstream s(str);
		std::string opt;
		while (std::getline(s, opt, ',')) {
			auto it = mountFlags.find(opt);
			if (it != mountFlags.end())
				mnt_flags |= it->second;
		}
	}
	void parsePropagation(const std::string& pg) {
		if (pg.find("master:") == 0) {
			propagation.type = PropagationType::SLAVE;
			propagation.id = std::stoi(pg.substr(7));
		} else if (pg.find("shared:") == 0) {
			propagation.type = PropagationType::SHARED;
			propagation.id = std::stoi(pg.substr(7));
		} else if (pg == "unbindable") {
			propagation.type = PropagationType::UNBINDABLE;
		} else if (pg == "private") {
			propagation.type = PropagationType::PRIVATE;
		}
	}
	void parseOptions(const std::string& opt) {
		mnt_opts.parse(opt);
	}
	void parseMajorMinor(const std::string& mmstr) {
		size_t sep = mmstr.find(':');
		if (sep != std::string::npos) {
			int major = std::stoi(mmstr.substr(0, sep));
			int minor = std::stoi(mmstr.substr(sep + 1));
			dev = makedev(major, minor);
		} else {
			dev = 0;
		}
	}
}

class
MountInfo
{
public:
explicit MountInfo(const std::string& line) {}
~MountInfo() {}
[[nodiscard]] int getMountId() const {}
[[nodiscard]] int getParentId() const {}
[[nodiscard]] dev_t getDev() const {}
[[nodiscard]] const std::string & getRoot() const {}
[[nodiscard]] const std::string & getMountPoint() const {}
[[nodiscard]] MountFlags getFlags() const {}
[[nodiscard]] const MountPropagation & getPropagation() const {}
[[nodiscard]] const std::string & getFsType() const {}
[[nodiscard]] const std::string & getMountSource() const {}
[[nodiscard]] const MountOptions & getMountOptions() const {}
private:
int mnt_id;
int mnt_parent_id;
dev_t dev = 0;
std::string root;
std::string mnt_pnt;
MountFlags mnt_flags = MountFlags(0);
MountPropagation propagation;
std::string fs_type;
std::string mnt_src;
MountOptions mnt_opts;
void parseFlags(const std::string& str) {}
void parsePropagation(const std::string& pg) {}
void parseOptions(const std::string& opt) {}
void parseMajorMinor(const std::string& mmstr) {}
};

This code segment functions to: Parse structured system mount information from text-based input, extracting and converting device identifiers, access flags, propagation settings, and configuration parameters into programmatic data representations.

```cpp
[TASK_BEGIN]
class
MountInfo
{
public:
explicit MountInfo(const std::string& line) {
    std::istringstream iss(line);
    std::vector<std::string> tokens;
    std::string token;

    while (iss >> token) {
        tokens.push_back(token);
    }

    if (tokens.size() < 10) return;

    mnt_id = std::stoi(tokens[0]);
    mnt_parent_id = std::stoi(tokens[1]);
    parseMajorMinor(tokens[2]);
    root = unescape(tokens[3]);
    mnt_pnt = unescape(tokens[4]);
    parseFlags(tokens[5]);

    size_t hyphen_pos = tokens.size();
    for (size_t i = 6; i < tokens.size(); ++i) {
        if (tokens[i] == "-") {
            hyphen_pos = i;
            break;
        }
    }

    for (size_t i = 6; i < hyphen_pos; ++i) {
        parsePropagation(tokens[i]);
    }

    fs_type = tokens[hyphen_pos + 1];
    mnt_src = unescape(tokens[hyphen_pos + 2]);
    parseOptions(tokens[hyphen_pos + 3]);
}

~MountInfo() = default;

[[nodiscard]] int getMountId() const { return mnt_id; }
[[nodiscard]] int getParentId() const { return mnt_parent_id; }
[[nodiscard]] dev_t getDev() const { return dev; }
[[nodiscard]] const std::string & getRoot() const { return root; }
[[nodiscard]] const std::string & getMountPoint() const { return mnt_pnt; }
[[nodiscard]] MountFlags getFlags() const { return mnt_flags; }
[[nodiscard]] const MountPropagation & getPropagation() const { return propagation; }
[[nodiscard]] const std::string & getFsType() const { return fs_type; }
[[nodiscard]] const std::string & getMountSource() const { return mnt_src; }
[[nodiscard]] const MountOptions & getMountOptions() const { return mnt_opts; }

private:
int mnt_id;
int mnt_parent_id;
dev_t dev = 0;
std::string root;
std::string mnt_pnt;
MountFlags mnt_flags = MountFlags(0);
MountPropagation propagation;
std::string fs_type;
std::string mnt_src;
MountOptions mnt_opts;

static std::string unescape(const std::string& s) {
    std::string res;
    for (size_t i = 0; i < s.size(); ++i) {
        if (s[i] == '\\' && i+3 < s.size() &&
            isdigit(s[i+1]) && isdigit(s[i+2]) && isdigit(s[i+3])) {
            int c = (s[i+1]-'0')*64 + (s[i+2]-'0')*8 + (s[i+3]-'0');
            res += static_cast<char>(c);
            i += 3;
        } else {
            res += s[i];
        }
    }
    return res;
}

void parseFlags(const std::string& str) {
    std::istringstream s(str);
    std::string opt;
    while (std::getline(s, opt, ',')) {
        auto it = mountFlags.find(opt);
        if (it != mountFlags.end()) {
            mnt_flags |= it->second;
        }
    }
}

void parsePropagation(const std::string& pg) {
    size_t colon = pg.find(':');
    std::string type = pg.substr(0, colon);
    std::string id_str = (colon != std::string::npos) ? pg.substr(colon+1) : "";

    if (type == "shared") propagation.type = PropagationType::SHARED;
    else if (type == "private") propagation.type = PropagationType::PRIVATE;
    else if (type == "slave") propagation.type = PropagationType::SLAVE;
    else if (type == "unbindable") propagation.type = PropagationType::UNBINDABLE;
    else return;

    if (!id_str.empty()) {
        try {
            propagation.id = std::stoi(id_str);
        } catch (...) {}
    }
}

void parseOptions(const std::string& opt) {
    mnt_opts.parse(opt);
}

void parseMajorMinor(const std::string& mmstr) {
    size_t colon = mmstr.find(':');
    if (colon == std::string::npos) return;

    try {
        unsigned major = std::stoul(mmstr.substr(0, colon));
        unsigned minor = std::stoul(mmstr.substr(colon+1));
        dev = makedev(major, minor);
    } catch (...) {}
}
};
[TASK_END]
```