Consolidate for_each implementation into Rust

This commit is contained in:
topjohnwu
2025-08-24 15:13:56 -07:00
committed by John Wu
parent 78f5cd55c7
commit e91fc75d86
12 changed files with 103 additions and 103 deletions

View File

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

View File

@@ -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<bool(string_view)> &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<bool(string_view)> &fn) {
if (auto fp = open_file(file, "re"))
file_readline(trim, fp.get(), fn);
}
void file_readline(const char *file, const function<bool(string_view)> &fn) {
file_readline(false, file, fn);
}
void parse_prop_file(FILE *fp, const function<bool(string_view, string_view)> &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<bool(string_view, string_view)> &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; });
}

View File

@@ -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<bool(std::string_view)> &fn);
void file_readline(bool trim, const char *file, const std::function<bool(std::string_view)> &fn);
void file_readline(const char *file, const std::function<bool(std::string_view)> &fn);
void parse_prop_file(FILE *fp, const std::function<bool(std::string_view, std::string_view)> &fn);
void parse_prop_file(const char *file,
const std::function<bool(std::string_view, std::string_view)> &fn);
std::string resolve_preinit_dir(const char *base_dir);
// Functor = function<bool(string_view)>
template <typename Functor>
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<bool(string_view, string_view)>
template <typename Functor>
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<char *>(key.data()) + key.size()) = '\0';
*(const_cast<char *>(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<FILE, decltype(&fclose)>;
using sDIR = std::unique_ptr<DIR, decltype(&closedir)>;
sDIR make_dir(DIR *dp);

View File

@@ -57,12 +57,12 @@ impl<T: Read + Seek> ReadSeekExt for T {
}
pub trait BufReadExt {
fn foreach_lines<F: FnMut(&mut String) -> bool>(&mut self, f: F);
fn foreach_props<F: FnMut(&str, &str) -> bool>(&mut self, f: F);
fn for_each_line<F: FnMut(&mut String) -> bool>(&mut self, f: F);
fn for_each_prop<F: FnMut(&str, &str) -> bool>(&mut self, f: F);
}
impl<T: BufRead> BufReadExt for T {
fn foreach_lines<F: FnMut(&mut String) -> bool>(&mut self, mut f: F) {
fn for_each_line<F: FnMut(&mut String) -> bool>(&mut self, mut f: F) {
let mut buf = String::new();
loop {
match self.read_line(&mut buf) {
@@ -81,8 +81,11 @@ impl<T: BufRead> BufReadExt for T {
}
}
fn foreach_props<F: FnMut(&str, &str) -> bool>(&mut self, mut f: F) {
self.foreach_lines(|line| {
fn for_each_prop<F: FnMut(&str, &str) -> 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<MountInfo> {
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()

View File

@@ -47,6 +47,12 @@ pub mod ffi {
fn mut_u8_patch(buf: &mut [u8], from: &[u8], to: &[u8]) -> Vec<usize>;
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"]

View File

@@ -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<bool(rust::Str, rust::Str)> {
using std::function<bool(rust::Str, rust::Str)>::function;
bool call(rust::Str a, rust::Str b) const {
return operator()(a, b);
}
};
struct FnBoolString : public std::function<bool(rust::String&)> {
using std::function<bool(rust::String&)>::function;
bool call(rust::String &s) const {
return operator()(s);
}
};

View File

@@ -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::<i32>().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;

View File

@@ -88,7 +88,7 @@ fn read_certificate(apk: &mut File, version: i32) -> Vec<u8> {
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::<i32>().unwrap_or(0);
false

View File

@@ -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());

View File

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

View File

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

View File

@@ -287,7 +287,7 @@ impl SePolicy {
}
fn load_rules_from_reader<T: BufRead>(&mut self, reader: &mut T) {
reader.foreach_lines(|line| {
reader.for_each_line(|line| {
self.parse_statement(line);
true
});