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 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;
}
};

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;
}

/* 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

This code segment functions to: Read and parse configuration data from persistent storage, filtering invalid entries while maintaining insertion order and providing direct access mappings.

```cpp
[TASK_BEGIN]
bool loadFromFile() {
    std::ifstream file(filePath);
    if (!file.is_open()) {
        LOGE("PropertyManager: Could not open %s for loading.", filePath.c_str());
        return false;
    }

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

    std::string line;
    while (std::getline(file, line)) {
        size_t equals_pos = line.find('=');
        if (equals_pos == std::string::npos) continue;

        std::string key = line.substr(0, equals_pos);
        std::string value = line.substr(equals_pos + 1);
        size_t new_index = orderedProps.size();
        orderedProps.emplace_back(key, value);
        keyIndex[key] = new_index;
    }

    file.close();
    return true;
}
[TASK_END]
```


This code segment functions to: Read structured data from persistent storage, parse key-value pairs while ignoring invalid entries, and populate in-memory data structures while maintaining insertion order for subsequent operations.

```cpp
[TASK_BEGIN]
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 pos = line.find('=');
        if (pos == std::string::npos) continue;

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

        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;
        }
    }

    return true;
}
[TASK_END]
```


#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::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);
}

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)};
}

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;
}
};

std::pair<dev_t, ino_t> devinobymap(const std::string& lib, bool useFind = false, unsigned int *ln = nullptr) {}

This code segment functions to: Locate specific library entries within process memory mappings and retrieve associated filesystem identifiers by parsing hexadecimal device codes and inode numbers from matched entries.

```cpp
[TASK_BEGIN]
std::pair<dev_t, ino_t> devinobymap(const std::string& lib, bool useFind, unsigned int* ln) {
    std::ifstream maps("/proc/self/maps");
    std::string line;
    unsigned int current_line = 0;
    while (std::getline(maps, line)) {
        ++current_line;
        std::istringstream iss(line);
        std::vector<std::string> tokens;
        std::string token;
        while (iss >> token)
            tokens.push_back(token);
        if (tokens.size() < 6)
            continue;

        std::string path;
        for (size_t i = 5; i < tokens.size(); ++i) {
            if (i != 5) path += ' ';
            path += tokens[i];
        }

        if (path.find(lib) != std::string::npos) {
            struct stat st{};
            if (stat(path.c_str(), &st) == 0) {
                if (ln) *ln = current_line;
                return {st.st_dev, st.st_ino};
            }
        }
    }

    if (useFind) {
        auto res = devinoby(lib.c_str());
        if (res)
            return res.value();
    }

    return {0, 0};
}
[TASK_END]
```


#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:

	~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;
}

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]);
	}

explicit MountInfo(const std::string& line) {}

This code segment functions to: Process a structured text entry by tokenizing components, extract numerical identifiers and device information, populate object properties with parsed values including access flags and system paths, and organize hierarchical relationships between mount points based on positional elements in the input data.

```cpp
[TASK_BEGIN]
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() < 9) {
        return;
    }

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

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

    if (sep == 0 || sep + 3 >= tokens.size()) {
        return;
    }

    parseFlags(tokens[5]);

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

    fs_type = tokens[sep + 1];
    mnt_src = tokens[sep + 2];
    parseOptions(tokens[sep + 3]);
}
[TASK_END]
```