From e91fc75d869066b5e4228097315fa94d2e4df779 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 24 Aug 2025 15:13:56 -0700 Subject: [PATCH] Consolidate for_each implementation into Rust --- native/src/base/cxx_extern.rs | 24 +++++++++++--- native/src/base/files.cpp | 56 -------------------------------- native/src/base/files.hpp | 26 +++++++++++---- native/src/base/files.rs | 15 +++++---- native/src/base/lib.rs | 8 +++++ native/src/base/misc.hpp | 18 +++++++++- native/src/core/daemon.rs | 8 ++--- native/src/core/package.rs | 2 +- native/src/core/su/su.cpp | 3 +- native/src/init/rootdir.cpp | 42 ++++++++++++------------ native/src/init/rootdir.rs | 2 +- native/src/sepolicy/statement.rs | 2 +- 12 files changed, 103 insertions(+), 103 deletions(-) diff --git a/native/src/base/cxx_extern.rs b/native/src/base/cxx_extern.rs index 8b4fae3fb..e39a35348 100644 --- a/native/src/base/cxx_extern.rs +++ b/native/src/base/cxx_extern.rs @@ -1,15 +1,20 @@ // Functions in this file are only for exporting to C++, DO NOT USE IN RUST -use std::os::fd::{BorrowedFd, OwnedFd, RawFd}; +use std::fs::File; +use std::io::BufReader; +use std::mem::ManuallyDrop; +use std::ops::DerefMut; +use std::os::fd::{BorrowedFd, FromRawFd, OwnedFd, RawFd}; use cfg_if::cfg_if; -use libc::{c_char, mode_t}; +use libc::{O_RDONLY, c_char, mode_t}; +use crate::ffi::{FnBoolStrStr, FnBoolString}; use crate::files::map_file_at; pub(crate) use crate::xwrap::*; use crate::{ - CxxResultExt, Directory, OsResultStatic, Utf8CStr, clone_attr, cstr, fclone_attr, fd_path, - map_fd, map_file, slice_from_ptr, + BufReadExt, CxxResultExt, Directory, OsResultStatic, Utf8CStr, clone_attr, cstr, fclone_attr, + fd_path, map_fd, map_file, slice_from_ptr, }; pub(crate) fn fd_path_for_cxx(fd: RawFd, buf: &mut [u8]) -> isize { @@ -178,3 +183,14 @@ unsafe extern "C" fn str_ptr(this: &&Utf8CStr) -> *const u8 { unsafe extern "C" fn str_len(this: &&Utf8CStr) -> usize { this.len() } + +pub(crate) fn parse_prop_file_rs(name: &Utf8CStr, f: &FnBoolStrStr) { + if let Ok(file) = name.open(O_RDONLY) { + BufReader::new(file).for_each_prop(|key, value| f.call(key, value)) + } +} + +pub(crate) fn file_readline_rs(fd: RawFd, f: &FnBoolString) { + let mut fd = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) }); + BufReader::new(fd.deref_mut()).for_each_line(|line| f.call(line)); +} diff --git a/native/src/base/files.cpp b/native/src/base/files.cpp index 182157941..fc948dbe2 100644 --- a/native/src/base/files.cpp +++ b/native/src/base/files.cpp @@ -8,15 +8,6 @@ using namespace std; -int fd_pathat(int dirfd, const char *name, char *path, size_t size) { - if (fd_path(dirfd, byte_data(path, size)) < 0) - return -1; - auto len = strlen(path); - path[len] = '/'; - strscpy(path + len + 1, name, size - len - 1); - return 0; -} - void full_read(int fd, string &str) { char buf[4096]; for (ssize_t len; (len = xread(fd, buf, sizeof(buf))) > 0;) @@ -52,53 +43,6 @@ void write_zero(int fd, size_t size) { } } -void file_readline(bool trim, FILE *fp, const function &fn) { - size_t len = 1024; - char *buf = (char *) malloc(len); - char *start; - ssize_t read; - while ((read = getline(&buf, &len, fp)) >= 0) { - start = buf; - if (trim) { - while (read && "\n\r "sv.find(buf[read - 1]) != string::npos) - --read; - buf[read] = '\0'; - while (*start == ' ') - ++start; - } - if (!fn(start)) - break; - } - free(buf); -} - -void file_readline(bool trim, const char *file, const function &fn) { - if (auto fp = open_file(file, "re")) - file_readline(trim, fp.get(), fn); -} - -void file_readline(const char *file, const function &fn) { - file_readline(false, file, fn); -} - -void parse_prop_file(FILE *fp, const function &fn) { - file_readline(true, fp, [&](string_view line_view) -> bool { - char *line = (char *) line_view.data(); - if (line[0] == '#') - return true; - char *eql = strchr(line, '='); - if (eql == nullptr || eql == line) - return true; - *eql = '\0'; - return fn(line, eql + 1); - }); -} - -void parse_prop_file(const char *file, const function &fn) { - if (auto fp = open_file(file, "re")) - parse_prop_file(fp.get(), fn); -} - sDIR make_dir(DIR *dp) { return sDIR(dp, [](DIR *dp){ return dp ? closedir(dp) : 1; }); } diff --git a/native/src/base/files.hpp b/native/src/base/files.hpp index 751347b6c..35c5689bc 100644 --- a/native/src/base/files.hpp +++ b/native/src/base/files.hpp @@ -45,7 +45,6 @@ bool fclone_attr(int src, int dest); } // extern "C" -int fd_pathat(int dirfd, const char *name, char *path, size_t size); static inline ssize_t realpath( const char * __restrict__ path, char * __restrict__ buf, size_t bufsiz) { return canonical_path(path, buf, bufsiz); @@ -55,14 +54,27 @@ void full_read(const char *filename, std::string &str); std::string full_read(int fd); std::string full_read(const char *filename); void write_zero(int fd, size_t size); -void file_readline(bool trim, FILE *fp, const std::function &fn); -void file_readline(bool trim, const char *file, const std::function &fn); -void file_readline(const char *file, const std::function &fn); -void parse_prop_file(FILE *fp, const std::function &fn); -void parse_prop_file(const char *file, - const std::function &fn); std::string resolve_preinit_dir(const char *base_dir); +// Functor = function +template +void file_readline(int fd, Functor &&fn) { + file_readline_rs(fd, [&](rust::String &line) -> bool { + return fn(std::string_view(line.c_str(), line.size())); + }); +} + +// Functor = function +template +void parse_prop_file(const char *file, Functor &&fn) { + parse_prop_file_rs(file, [&](rust::Str key, rust::Str val) -> bool { + // Null terminate all strings + *(const_cast(key.data()) + key.size()) = '\0'; + *(const_cast(val.data()) + val.size()) = '\0'; + return fn(std::string_view(key.data(), key.size()), std::string_view(val.data(), val.size())); + }); +} + using sFILE = std::unique_ptr; using sDIR = std::unique_ptr; sDIR make_dir(DIR *dp); diff --git a/native/src/base/files.rs b/native/src/base/files.rs index 77431e4d0..1f88b45d9 100644 --- a/native/src/base/files.rs +++ b/native/src/base/files.rs @@ -57,12 +57,12 @@ impl ReadSeekExt for T { } pub trait BufReadExt { - fn foreach_lines bool>(&mut self, f: F); - fn foreach_props bool>(&mut self, f: F); + fn for_each_line bool>(&mut self, f: F); + fn for_each_prop bool>(&mut self, f: F); } impl BufReadExt for T { - fn foreach_lines bool>(&mut self, mut f: F) { + fn for_each_line bool>(&mut self, mut f: F) { let mut buf = String::new(); loop { match self.read_line(&mut buf) { @@ -81,8 +81,11 @@ impl BufReadExt for T { } } - fn foreach_props bool>(&mut self, mut f: F) { - self.foreach_lines(|line| { + fn for_each_prop bool>(&mut self, mut f: F) { + self.for_each_line(|line| { + // Reserve an additional byte, because this string will be manually + // null terminated on the C++ side, and it may need more space. + line.reserve(1); let line = line.trim(); if line.starts_with('#') { return true; @@ -874,7 +877,7 @@ pub fn parse_mount_info(pid: &str) -> Vec { let mut res = vec![]; let mut path = format!("/proc/{pid}/mountinfo"); if let Ok(file) = Utf8CStr::from_string(&mut path).open(O_RDONLY | O_CLOEXEC) { - BufReader::new(file).foreach_lines(|line| { + BufReader::new(file).for_each_line(|line| { parse_mount_info_line(line) .map(|info| res.push(info)) .is_some() diff --git a/native/src/base/lib.rs b/native/src/base/lib.rs index d5e492a87..62f70d2bd 100644 --- a/native/src/base/lib.rs +++ b/native/src/base/lib.rs @@ -47,6 +47,12 @@ pub mod ffi { fn mut_u8_patch(buf: &mut [u8], from: &[u8], to: &[u8]) -> Vec; fn fork_dont_care() -> i32; + + type FnBoolStrStr; + fn call(self: &FnBoolStrStr, key: &str, value: &str) -> bool; + + type FnBoolString; + fn call(self: &FnBoolString, key: &mut String) -> bool; } extern "Rust" { @@ -56,6 +62,8 @@ pub mod ffi { fn set_log_level_state_cxx(level: LogLevelCxx, enabled: bool); fn exit_on_error(b: bool); fn cmdline_logging(); + fn parse_prop_file_rs(name: Utf8CStrRef, f: &FnBoolStrStr); + fn file_readline_rs(fd: i32, f: &FnBoolString); } #[namespace = "rust"] diff --git a/native/src/base/misc.hpp b/native/src/base/misc.hpp index ebd3de1fb..501eb0317 100644 --- a/native/src/base/misc.hpp +++ b/native/src/base/misc.hpp @@ -283,7 +283,8 @@ struct Utf8CStr { const char *c_str() const { return this->data(); } size_t size() const { return this->length(); } bool empty() const { return this->length() == 0 ; } - operator std::string_view() { return {data(), length()}; } + operator std::string_view() const { return {data(), length()}; } + bool operator==(std::string_view rhs) const { return std::string_view{data(), length()} == rhs; } private: #pragma clang diagnostic push @@ -293,3 +294,18 @@ private: }; } // namespace rust + +// Bindings for std::function to be callable from Rust +struct FnBoolStrStr : public std::function { + using std::function::function; + bool call(rust::Str a, rust::Str b) const { + return operator()(a, b); + } +}; + +struct FnBoolString : public std::function { + using std::function::function; + bool call(rust::String &s) const { + return operator()(s); + } +}; \ No newline at end of file diff --git a/native/src/core/daemon.rs b/native/src/core/daemon.rs index f68c55d92..f86db6755 100644 --- a/native/src/core/daemon.rs +++ b/native/src/core/daemon.rs @@ -243,7 +243,7 @@ pub fn daemon_entry() { .join_path(MAIN_CONFIG); let mut is_recovery = false; if let Ok(main_config) = tmp_path.open(O_RDONLY | O_CLOEXEC) { - BufReader::new(main_config).foreach_props(|key, val| { + BufReader::new(main_config).for_each_prop(|key, val| { if key == "RECOVERYMODE" { is_recovery = val == "true"; return false; @@ -255,7 +255,7 @@ pub fn daemon_entry() { let mut sdk_int = -1; if let Ok(build_prop) = cstr!("/system/build.prop").open(O_RDONLY | O_CLOEXEC) { - BufReader::new(build_prop).foreach_props(|key, val| { + BufReader::new(build_prop).for_each_prop(|key, val| { if key == "ro.build.version.sdk" { sdk_int = val.parse::().unwrap_or(-1); return false; @@ -290,7 +290,7 @@ pub fn daemon_entry() { // Cleanup pre-init mounts tmp_path.append_path(ROOTMNT); if let Ok(mount_list) = tmp_path.open(O_RDONLY | O_CLOEXEC) { - BufReader::new(mount_list).foreach_lines(|line| { + BufReader::new(mount_list).for_each_line(|line| { line.truncate(line.trim_end().len()); let item = Utf8CStr::from_string(line); item.unmount().log_ok(); @@ -336,7 +336,7 @@ fn switch_cgroup(cgroup: &str, pid: i32) { fn check_data() -> bool { if let Ok(file) = cstr!("/proc/mounts").open(O_RDONLY | O_CLOEXEC) { let mut mnt = false; - BufReader::new(file).foreach_lines(|line| { + BufReader::new(file).for_each_line(|line| { if line.contains(" /data ") && !line.contains("tmpfs") { mnt = true; return false; diff --git a/native/src/core/package.rs b/native/src/core/package.rs index 671afbc36..f7b674031 100644 --- a/native/src/core/package.rs +++ b/native/src/core/package.rs @@ -88,7 +88,7 @@ fn read_certificate(apk: &mut File, version: i32) -> Vec { apk.read_exact(&mut comment)?; let mut comment = Cursor::new(&comment); let mut apk_ver = 0; - comment.foreach_props(|k, v| { + comment.for_each_prop(|k, v| { if k == "versionCode" { apk_ver = v.parse::().unwrap_or(0); false diff --git a/native/src/core/su/su.cpp b/native/src/core/su/su.cpp index b281ccce2..4041e47b8 100644 --- a/native/src/core/su/su.cpp +++ b/native/src/core/su/su.cpp @@ -300,7 +300,8 @@ static bool proc_is_restricted(pid_t pid) { auto bnd = "CapBnd:"sv; uint32_t data[_LINUX_CAPABILITY_U32S_3] = {}; ssprintf(buf, sizeof(buf), "/proc/%d/status", pid); - file_readline(buf, [&](string_view line) -> bool { + owned_fd status_fd = xopen(buf, O_RDONLY | O_CLOEXEC); + file_readline(status_fd, [&](string_view line) -> bool { if (line.starts_with(bnd)) { auto p = line.begin(); advance(p, bnd.size()); diff --git a/native/src/init/rootdir.cpp b/native/src/init/rootdir.cpp index cef17794e..b00bb6690 100644 --- a/native/src/init/rootdir.cpp +++ b/native/src/init/rootdir.cpp @@ -60,14 +60,14 @@ static bool patch_rc_scripts(const char *src_path, const char *tmp_path, bool wr // First patch init.rc { - auto src = xopen_file(xopenat(src_fd, INIT_RC, O_RDONLY | O_CLOEXEC, 0), "re"); - if (!src) return false; + owned_fd src_rc = xopenat(src_fd, INIT_RC, O_RDONLY | O_CLOEXEC, 0); + if (src_rc < 0) return false; if (writable) unlinkat(src_fd, INIT_RC, 0); - auto dest = xopen_file( + auto dest_rc = xopen_file( xopenat(dest_fd, INIT_RC, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0), "we"); - if (!dest) return false; + if (!dest_rc) return false; LOGD("Patching " INIT_RC " in %s\n", src_path); - file_readline(false, src.get(), [&dest](string_view line) -> bool { + file_readline(src_rc, [&dest_rc](string_view line) -> bool { // Do not start vaultkeeper if (str_contains(line, "start vaultkeeper")) { LOGD("Remove vaultkeeper\n"); @@ -76,59 +76,59 @@ static bool patch_rc_scripts(const char *src_path, const char *tmp_path, bool wr // Do not run flash_recovery if (line.starts_with("service flash_recovery")) { LOGD("Remove flash_recovery\n"); - fprintf(dest.get(), "service flash_recovery /system/bin/true\n"); + fprintf(dest_rc.get(), "service flash_recovery /system/bin/true\n"); return true; } // Samsung's persist.sys.zygote.early will cause Zygote to start before post-fs-data if (line.starts_with("on property:persist.sys.zygote.early=")) { LOGD("Invalidate persist.sys.zygote.early\n"); - fprintf(dest.get(), "on property:persist.sys.zygote.early.xxxxx=true\n"); + fprintf(dest_rc.get(), "on property:persist.sys.zygote.early.xxxxx=true\n"); return true; } // Else just write the line - fprintf(dest.get(), "%s", line.data()); + fprintf(dest_rc.get(), "%s", line.data()); return true; }); - fprintf(dest.get(), "\n"); + fprintf(dest_rc.get(), "\n"); // Inject custom rc scripts for (auto &script : rc_list) { // Replace template arguments of rc scripts with dynamic paths replace_all(script, "${MAGISKTMP}", tmp_path); - fprintf(dest.get(), "\n%s\n", script.data()); + fprintf(dest_rc.get(), "\n%s\n", script.data()); } rc_list.clear(); // Inject Magisk rc scripts - rust::inject_magisk_rc(fileno(dest.get()), tmp_path); + rust::inject_magisk_rc(fileno(dest_rc.get()), tmp_path); - fclone_attr(fileno(src.get()), fileno(dest.get())); + fclone_attr(src_rc, fileno(dest_rc.get())); } // Then patch init.zygote*.rc for (dirent *entry; (entry = readdir(src_dir.get()));) { auto name = std::string_view(entry->d_name); if (!name.starts_with("init.zygote") || !name.ends_with(".rc")) continue; - auto src = xopen_file(xopenat(src_fd, name.data(), O_RDONLY | O_CLOEXEC, 0), "re"); - if (!src) continue; + owned_fd src_rc = xopenat(src_fd, name.data(), O_RDONLY | O_CLOEXEC, 0); + if (src_rc < 0) continue; if (writable) unlinkat(src_fd, name.data(), 0); - auto dest = xopen_file( + auto dest_rc = xopen_file( xopenat(dest_fd, name.data(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0), "we"); - if (!dest) continue; + if (!dest_rc) continue; LOGD("Patching %s in %s\n", name.data(), src_path); - file_readline(false, src.get(), [&dest, &tmp_path](string_view line) -> bool { + file_readline(src_rc, [&dest_rc, &tmp_path](string_view line) -> bool { if (line.starts_with("service zygote ")) { LOGD("Inject zygote restart\n"); - fprintf(dest.get(), "%s", line.data()); - fprintf(dest.get(), + fprintf(dest_rc.get(), "%s", line.data()); + fprintf(dest_rc.get(), " onrestart exec " MAGISK_PROC_CON " 0 0 -- %s/magisk --zygote-restart\n", tmp_path); return true; } - fprintf(dest.get(), "%s", line.data()); + fprintf(dest_rc.get(), "%s", line.data()); return true; }); - fclone_attr(fileno(src.get()), fileno(dest.get())); + fclone_attr(src_rc, fileno(dest_rc.get())); } return faccessat(src_fd, "init.fission_host.rc", F_OK, 0) == 0; diff --git a/native/src/init/rootdir.rs b/native/src/init/rootdir.rs index 788847e69..242ba0081 100644 --- a/native/src/init/rootdir.rs +++ b/native/src/init/rootdir.rs @@ -46,7 +46,7 @@ impl MagiskInit { pub(crate) fn parse_config_file(&mut self) { if let Ok(fd) = cstr!("/data/.backup/.magisk").open(O_RDONLY) { let mut reader = BufReader::new(fd); - reader.foreach_props(|key, val| { + reader.for_each_prop(|key, val| { if key == "PREINITDEVICE" { self.preinit_dev = val.to_string(); return false; diff --git a/native/src/sepolicy/statement.rs b/native/src/sepolicy/statement.rs index 9922cd394..8db9b2fee 100644 --- a/native/src/sepolicy/statement.rs +++ b/native/src/sepolicy/statement.rs @@ -287,7 +287,7 @@ impl SePolicy { } fn load_rules_from_reader(&mut self, reader: &mut T) { - reader.foreach_lines(|line| { + reader.for_each_line(|line| { self.parse_statement(line); true });