Make Utf8CStr a first class citizen in C++ codebase

Utf8CStr is in many cases a better string view class than
std::string_view, because it provides "view" access to a string buffer
that is guaranteed to be null terminated. It also has the additional
benefit of being UTF-8 verified and can seemlessly cross FFI boundaries.

We would want to start use more Utf8CStr in our existing C++ codebase.
This commit is contained in:
topjohnwu
2025-08-25 14:53:49 -07:00
committed by John Wu
parent e2abb648ac
commit 2e4fa6864c
25 changed files with 105 additions and 124 deletions

View File

@@ -273,7 +273,7 @@ static int fmt_and_log_with_rs(LogLevel level, const char *fmt, va_list ap) {
buf[0] = '\0';
// Fortify logs when a fatal error occurs. Do not run through fortify again
int len = std::min(__call_bypassing_fortify(vsnprintf)(buf, sz, fmt, ap), sz - 1);
log_with_rs(level, rust::Utf8CStr(buf, len + 1));
log_with_rs(level, Utf8CStr(buf, len + 1));
return len;
}
@@ -415,18 +415,18 @@ string resolve_preinit_dir(const char *base_dir) {
// FFI for Utf8CStr
extern "C" void cxx$utf8str$new(rust::Utf8CStr *self, const void *s, size_t len);
extern "C" const char *cxx$utf8str$ptr(const rust::Utf8CStr *self);
extern "C" size_t cxx$utf8str$len(const rust::Utf8CStr *self);
extern "C" void cxx$utf8str$new(Utf8CStr *self, const void *s, size_t len);
extern "C" const char *cxx$utf8str$ptr(const Utf8CStr *self);
extern "C" size_t cxx$utf8str$len(const Utf8CStr *self);
rust::Utf8CStr::Utf8CStr(const char *s, size_t len) {
Utf8CStr::Utf8CStr(const char *s, size_t len) {
cxx$utf8str$new(this, s, len);
}
const char *rust::Utf8CStr::data() const {
const char *Utf8CStr::data() const {
return cxx$utf8str$ptr(this);
}
size_t rust::Utf8CStr::length() const {
size_t Utf8CStr::length() const {
return cxx$utf8str$len(this);
}

View File

@@ -369,7 +369,7 @@ impl AsRef<Utf8CStr> for Utf8CStr {
// Notice that we only implement ExternType on Utf8CStr *reference*
unsafe impl ExternType for &Utf8CStr {
type Id = type_id!("rust::Utf8CStr");
type Id = type_id!("Utf8CStr");
type Kind = cxx::kind::Trivial;
}

View File

@@ -9,7 +9,7 @@ use std::os::fd::{BorrowedFd, FromRawFd, OwnedFd, RawFd};
use cfg_if::cfg_if;
use libc::{O_RDONLY, c_char, mode_t};
use crate::ffi::{FnBoolStrStr, FnBoolString};
use crate::ffi::{FnBoolStr, FnBoolStrStr};
use crate::files::map_file_at;
pub(crate) use crate::xwrap::*;
use crate::{
@@ -183,7 +183,7 @@ pub(crate) fn parse_prop_file_rs(name: &Utf8CStr, f: &FnBoolStrStr) {
}
}
pub(crate) fn file_readline_rs(fd: RawFd, f: &FnBoolString) {
pub(crate) fn file_readline_for_cxx(fd: RawFd, f: &FnBoolStr) {
let mut fd = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) });
BufReader::new(fd.deref_mut()).for_each_line(|line| f.call(line));
BufReader::new(fd.deref_mut()).for_each_line(|line| f.call(Utf8CStr::from_string(line)));
}

View File

@@ -38,22 +38,18 @@ std::string full_read(const char *filename);
void write_zero(int fd, size_t size);
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)>
// Functor = function<bool(Utf8CStr, Utf8CStr)>
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
// We perform the null termination here in C++ because it's very difficult to do it
// right in Rust due to pointer provenance. Trying to dereference a pointer without
// the correct provenance in Rust, even in unsafe code, is undefined behavior.
// However on the C++ side, there are fewer restrictions on pointers, so the const_cast here
// will not trigger UB in the compiler.
*(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()));
return fn(Utf8CStr(key.data(), key.size() + 1), Utf8CStr(val.data(), val.size() + 1));
});
}

View File

@@ -41,7 +41,6 @@ pub mod ffi {
unsafe extern "C++" {
include!("misc.hpp");
#[namespace = "rust"]
#[cxx_name = "Utf8CStr"]
type Utf8CStrRef<'a> = &'a crate::cstr::Utf8CStr;
@@ -51,8 +50,8 @@ pub mod ffi {
type FnBoolStrStr;
fn call(self: &FnBoolStrStr, key: &str, value: &str) -> bool;
type FnBoolString;
fn call(self: &FnBoolString, key: &mut String) -> bool;
type FnBoolStr;
fn call(self: &FnBoolStr, key: Utf8CStrRef) -> bool;
}
extern "Rust" {
@@ -63,7 +62,8 @@ pub mod ffi {
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);
#[cxx_name = "file_readline"]
fn file_readline_for_cxx(fd: i32, f: &FnBoolStr);
}
#[namespace = "rust"]

View File

@@ -136,21 +136,6 @@ int parse_int(std::string_view s);
using thread_entry = void *(*)(void *);
extern "C" int new_daemon_thread(thread_entry entry, void *arg = nullptr);
static inline bool str_contains(std::string_view s, std::string_view ss) {
return s.find(ss) != std::string::npos;
}
static inline bool str_starts(std::string_view s, std::string_view ss) {
return s.size() >= ss.size() && s.compare(0, ss.size(), ss) == 0;
}
static inline bool str_ends(std::string_view s, std::string_view ss) {
return s.size() >= ss.size() && s.compare(s.size() - ss.size(), std::string::npos, ss) == 0;
}
static inline std::string ltrim(std::string &&s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
return !std::isspace(ch);
}));
return std::move(s);
}
static inline std::string rtrim(std::string &&s) {
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
return !std::isspace(ch) && ch != '\0';
@@ -222,8 +207,6 @@ constexpr auto operator+(T e) noexcept ->
return static_cast<std::underlying_type_t<T>>(e);
}
namespace rust {
struct Utf8CStr {
const char *data() const;
size_t length() const;
@@ -236,8 +219,9 @@ 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() const { return {data(), length()}; }
bool operator==(std::string_view rhs) const { return std::string_view{data(), length()} == rhs; }
std::string_view sv() const { return {data(), length()}; }
operator std::string_view() const { return sv(); }
bool operator==(std::string_view rhs) const { return sv() == rhs; }
private:
#pragma clang diagnostic push
@@ -246,8 +230,6 @@ private:
#pragma clang diagnostic pop
};
} // namespace rust
// Bindings for std::function to be callable from Rust
using CxxFnBoolStrStr = std::function<bool(rust::Str, rust::Str)>;
@@ -257,10 +239,10 @@ struct FnBoolStrStr : public CxxFnBoolStrStr {
return operator()(a, b);
}
};
using CxxFnBoolString = std::function<bool(rust::String&)>;
struct FnBoolString : public CxxFnBoolString {
using CxxFnBoolString::function;
bool call(rust::String &s) const {
using CxxFnBoolStr = std::function<bool(Utf8CStr)>;
struct FnBoolStr : public CxxFnBoolStr {
using CxxFnBoolStr::function;
bool call(Utf8CStr s) const {
return operator()(s);
}
};

View File

@@ -156,7 +156,7 @@ void dyn_img_hdr::dump_hdr_file() const {
}
void dyn_img_hdr::load_hdr_file() {
parse_prop_file(HEADER_FILE, [=, this](string_view key, string_view value) -> bool {
parse_prop_file(HEADER_FILE, [=, this](Utf8CStr key, Utf8CStr value) -> bool {
if (key == "name" && name()) {
memset(name(), 0, 16);
memcpy(name(), value.data(), value.length() > 15 ? 15 : value.length());
@@ -166,7 +166,7 @@ void dyn_img_hdr::load_hdr_file() {
if (value.length() > BOOT_ARGS_SIZE) {
memcpy(cmdline(), value.data(), BOOT_ARGS_SIZE);
auto len = std::min(value.length() - BOOT_ARGS_SIZE, (size_t) BOOT_EXTRA_ARGS_SIZE);
memcpy(extra_cmdline(), &value[BOOT_ARGS_SIZE], len);
memcpy(extra_cmdline(), value.data() + BOOT_ARGS_SIZE, len);
} else {
memcpy(cmdline(), value.data(), value.length());
}
@@ -583,7 +583,7 @@ bool boot_img::parse_image(const uint8_t *p, FileFormat type) {
return true;
}
int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp) {
int split_image_dtb(Utf8CStr filename, bool skip_decomp) {
mmap_data img(filename.data());
if (size_t off = find_dtb_offset(img.data(), img.size()); off > 0) {
@@ -603,7 +603,7 @@ int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp) {
}
}
int unpack(rust::Utf8CStr image, bool skip_decomp, bool hdr) {
int unpack(Utf8CStr image, bool skip_decomp, bool hdr) {
const boot_img boot(image.data());
if (hdr)
@@ -688,7 +688,7 @@ write_zero(fd, align_padding(lseek(fd, 0, SEEK_CUR) - off.header, page_size))
#define file_align() file_align_with(boot.hdr->page_size())
void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp) {
void repack(Utf8CStr src_img, Utf8CStr out_img, bool skip_comp) {
const boot_img boot(src_img.data());
fprintf(stderr, "Repack to boot image: [%s]\n", out_img.data());

View File

@@ -685,7 +685,7 @@ struct boot_img {
std::pair<const uint8_t *, dyn_img_hdr *> create_hdr(const uint8_t *addr, FileFormat type);
// Rust FFI
static std::unique_ptr<boot_img> create(rust::Utf8CStr name) { return std::make_unique<boot_img>(name.c_str()); }
static std::unique_ptr<boot_img> create(Utf8CStr name) { return std::make_unique<boot_img>(name.c_str()); }
rust::Slice<const uint8_t> get_payload() const { return payload; }
rust::Slice<const uint8_t> get_tail() const { return tail; }
bool is_signed() const { return flags[AVB1_SIGNED_FLAG]; }

View File

@@ -49,15 +49,11 @@ pub mod ffi {
}
unsafe extern "C++" {
include!("../base/include/base.hpp");
include!("magiskboot.hpp");
#[namespace = "rust"]
#[cxx_name = "Utf8CStr"]
type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>;
}
unsafe extern "C++" {
include!("magiskboot.hpp");
fn cleanup();
fn unpack(image: Utf8CStrRef, skip_decomp: bool, hdr: bool) -> i32;
fn repack(src_img: Utf8CStrRef, out_img: Utf8CStrRef, skip_comp: bool);

View File

@@ -1,5 +1,7 @@
#pragma once
#include <base.hpp>
#define HEADER_FILE "header"
#define KERNEL_FILE "kernel"
#define RAMDISK_FILE "ramdisk.cpio"
@@ -47,11 +49,11 @@
#define AVB_MAGIC "AVB0"
#define ZIMAGE_MAGIC "\x18\x28\x6f\x01"
enum class FileFormat : ::std::uint8_t;
enum class FileFormat : uint8_t;
int unpack(rust::Utf8CStr image, bool skip_decomp = false, bool hdr = false);
void repack(rust::Utf8CStr src_img, rust::Utf8CStr out_img, bool skip_comp = false);
int split_image_dtb(rust::Utf8CStr filename, bool skip_decomp = false);
int unpack(Utf8CStr image, bool skip_decomp = false, bool hdr = false);
void repack(Utf8CStr src_img, Utf8CStr out_img, bool skip_comp = false);
int split_image_dtb(Utf8CStr filename, bool skip_decomp = false);
void cleanup();
FileFormat check_fmt(const void *buf, size_t len);

View File

@@ -28,7 +28,7 @@ int main(int argc, char *argv[]) {
cmdline_logging();
init_argv0(argc, argv);
string_view argv0 = basename(argv[0]);
Utf8CStr argv0 = basename(argv[0]);
umask(0);
@@ -63,6 +63,6 @@ int main(int argc, char *argv[]) {
return app.fn(argc, argv);
}
}
fprintf(stderr, "%s: applet not found\n", argv0.data());
fprintf(stderr, "%s: applet not found\n", argv0.c_str());
return 1;
}

View File

@@ -389,7 +389,7 @@ int connect_daemon(int req, bool create) {
char buf[64];
xreadlink("/proc/self/exe", buf, sizeof(buf));
if (tmp[0] == '\0' || !str_starts(buf, tmp)) {
if (tmp[0] == '\0' || !string_view(buf).starts_with(tmp)) {
LOGE("Start daemon on magisk tmpfs\n");
close(fd);
return -1;

View File

@@ -90,9 +90,10 @@ static void crawl_procfs(const F &fn) {
}
}
static inline bool str_eql(string_view a, string_view b) { return a == b; }
static bool str_eql(string_view a, string_view b) { return a == b; }
static bool str_starts_with(string_view a, string_view b) { return a.starts_with(b); }
template<bool str_op(string_view, string_view) = &str_eql>
template<bool str_op(string_view, string_view) = str_eql>
static bool proc_name_match(int pid, string_view name) {
char buf[4019];
sprintf(buf, "/proc/%d/cmdline", pid);
@@ -111,7 +112,7 @@ bool proc_context_match(int pid, string_view context) {
sprintf(buf, "/proc/%d", pid);
if (lgetfilecon(buf, byte_data{ con, sizeof(con) })) {
return str_starts(con, context);
return string_view(con).starts_with(context);
}
return false;
}
@@ -173,7 +174,7 @@ static bool add_hide_set(const char *pkg, const char *proc) {
return true;
if (str_eql(pkg, ISOLATED_MAGIC)) {
// Kill all matching isolated processes
kill_process<&proc_name_match<str_starts>>(proc, true);
kill_process<&proc_name_match<str_starts_with>>(proc, true);
} else {
kill_process(proc);
}
@@ -411,7 +412,7 @@ bool is_deny_target(int uid, string_view process) {
if (app_id >= 90000) {
if (auto it = pkg_to_procs.find(ISOLATED_MAGIC); it != pkg_to_procs.end()) {
for (const auto &s : it->second) {
if (str_starts(process, s))
if (process.starts_with(s))
return true;
}
}

View File

@@ -88,10 +88,10 @@ void exec_task(std::function<void()> &&task);
void denylist_handler(int client, const sock_cred *cred);
// Scripting
void install_apk(rust::Utf8CStr apk);
void uninstall_pkg(rust::Utf8CStr pkg);
void exec_common_scripts(rust::Utf8CStr stage);
void exec_module_scripts(rust::Utf8CStr stage, const rust::Vec<ModuleInfo> &module_list);
void install_apk(Utf8CStr apk);
void uninstall_pkg(Utf8CStr pkg);
void exec_common_scripts(Utf8CStr stage);
void exec_module_scripts(Utf8CStr stage, const rust::Vec<ModuleInfo> &module_list);
void exec_script(const char *script);
void clear_pkg(const char *pkg, int user_id);
[[noreturn]] void install_module(const char *file);
@@ -109,8 +109,8 @@ void update_deny_flags(int uid, rust::Str process, uint32_t &flags);
void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode);
// Rust bindings
static inline rust::Utf8CStr get_magisk_tmp_rs() { return get_magisk_tmp(); }
static inline rust::String resolve_preinit_dir_rs(rust::Utf8CStr base_dir) {
static inline Utf8CStr get_magisk_tmp_rs() { return get_magisk_tmp(); }
static inline rust::String resolve_preinit_dir_rs(Utf8CStr base_dir) {
return resolve_preinit_dir(base_dir.c_str());
}
static inline void exec_script_rs(rust::Utf8CStr script) { exec_script(script.data()); }
static inline void exec_script_rs(Utf8CStr script) { exec_script(script.data()); }

View File

@@ -26,13 +26,13 @@ int set_prop(const char *name, const char *value, bool skip_svc = false);
void load_prop_file(const char *filename, bool skip_svc = false);
// Rust bindings
rust::String get_prop_rs(rust::Utf8CStr name, bool persist);
static inline int set_prop_rs(rust::Utf8CStr name, rust::Utf8CStr value, bool skip_svc) {
rust::String get_prop_rs(Utf8CStr name, bool persist);
static inline int set_prop_rs(Utf8CStr name, Utf8CStr value, bool skip_svc) {
return set_prop(name.data(), value.data(), skip_svc);
}
static inline void load_prop_file_rs(rust::Utf8CStr filename, bool skip_svc) {
static inline void load_prop_file_rs(Utf8CStr filename, bool skip_svc) {
load_prop_file(filename.data(), skip_svc);
}
static inline void prop_cb_exec(prop_cb &cb, rust::Utf8CStr name, rust::Utf8CStr value, uint32_t serial) {
static inline void prop_cb_exec(prop_cb &cb, Utf8CStr name, Utf8CStr value, uint32_t serial) {
cb.exec(name.data(), value.data(), serial);
}

View File

@@ -127,7 +127,6 @@ pub mod ffi {
}
unsafe extern "C++" {
#[namespace = "rust"]
#[cxx_name = "Utf8CStr"]
type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>;
#[cxx_name = "ucred"]

View File

@@ -143,6 +143,10 @@ template<> void prop_to_string<rust::String>::exec(const char *, const char *val
serial = s;
}
static bool str_starts(std::string_view s, std::string_view ss) {
return s.starts_with(ss);
}
static int set_prop(const char *name, const char *value, PropFlags flags) {
if (!check_legal_property_name(name))
return 1;
@@ -429,7 +433,7 @@ static StringType get_prop_impl(const char *name, bool persist) {
return get_prop<StringType>(name, flags);
}
rust::String get_prop_rs(rust::Utf8CStr name, bool persist) {
rust::String get_prop_rs(Utf8CStr name, bool persist) {
return get_prop_impl<rust::String>(name.data(), persist);
}

View File

@@ -74,7 +74,7 @@ if (pfs) { \
exit(0); \
}
void exec_common_scripts(rust::Utf8CStr stage) {
void exec_common_scripts(Utf8CStr stage) {
LOGI("* Running %s.d scripts\n", stage.c_str());
char path[4096];
char *name = path + sprintf(path, SECURE_DIR "/%s.d", stage.c_str());
@@ -116,12 +116,12 @@ static bool operator>(const timespec &a, const timespec &b) {
return a.tv_nsec > b.tv_nsec;
}
void exec_module_scripts(rust::Utf8CStr stage, const rust::Vec<ModuleInfo> &module_list) {
void exec_module_scripts(Utf8CStr stage, const rust::Vec<ModuleInfo> &module_list) {
LOGI("* Running module %s scripts\n", stage.c_str());
if (module_list.empty())
return;
bool pfs = (string_view) stage == "post-fs-data";
bool pfs = stage == "post-fs-data";
if (pfs) {
timespec now{};
clock_gettime(CLOCK_MONOTONIC, &now);
@@ -157,7 +157,7 @@ appops set %s REQUEST_INSTALL_PACKAGES allow
rm -f $APK
)EOF";
void install_apk(rust::Utf8CStr apk) {
void install_apk(Utf8CStr apk) {
setfilecon(apk.c_str(), MAGISK_FILE_CON);
char cmds[sizeof(install_script) + 4096];
ssprintf(cmds, sizeof(cmds), install_script, apk.c_str(), JAVA_PACKAGE_NAME);
@@ -170,7 +170,7 @@ log -t Magisk "pm_uninstall: $PKG"
log -t Magisk "pm_uninstall: $(pm uninstall $PKG 2>&1)"
)EOF";
void uninstall_pkg(rust::Utf8CStr pkg) {
void uninstall_pkg(Utf8CStr pkg) {
char cmds[sizeof(uninstall_script) + 256];
ssprintf(cmds, sizeof(cmds), uninstall_script, pkg.c_str());
exec_command_async("/system/bin/sh", "-c", cmds);

View File

@@ -301,7 +301,8 @@ static bool proc_is_restricted(pid_t pid) {
uint32_t data[_LINUX_CAPABILITY_U32S_3] = {};
ssprintf(buf, sizeof(buf), "/proc/%d/status", pid);
owned_fd status_fd = xopen(buf, O_RDONLY | O_CLOEXEC);
file_readline(status_fd, [&](string_view line) -> bool {
file_readline(status_fd, [&](Utf8CStr s) -> bool {
string_view line = s;
if (line.starts_with(bnd)) {
auto p = line.begin();
advance(p, bnd.size());

View File

@@ -15,23 +15,23 @@
#include "init-rs.hpp"
int magisk_proxy_main(int, char *argv[]);
rust::Utf8CStr backup_init();
Utf8CStr backup_init();
// Expose some constants to Rust
static inline rust::Utf8CStr split_plat_cil() {
static inline Utf8CStr split_plat_cil() {
return SPLIT_PLAT_CIL;
};
static inline rust::Utf8CStr preload_lib() {
static inline Utf8CStr preload_lib() {
return PRELOAD_LIB;
}
static inline rust::Utf8CStr preload_policy() {
static inline Utf8CStr preload_policy() {
return PRELOAD_POLICY;
}
static inline rust::Utf8CStr preload_ack() {
static inline Utf8CStr preload_ack() {
return PRELOAD_ACK;
}

View File

@@ -49,7 +49,6 @@ pub mod ffi {
unsafe extern "C++" {
include!("init.hpp");
#[namespace = "rust"]
#[cxx_name = "Utf8CStr"]
type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>;

View File

@@ -30,15 +30,15 @@ static void parse_device(devinfo *dev, const char *uevent) {
dev->devpath[0] = '\0';
dev->dmname[0] = '\0';
dev->devname[0] = '\0';
parse_prop_file(uevent, [=](string_view key, string_view value) -> bool {
parse_prop_file(uevent, [=](Utf8CStr key, Utf8CStr value) -> bool {
if (key == "MAJOR")
dev->major = parse_int(value.data());
dev->major = parse_int(value);
else if (key == "MINOR")
dev->minor = parse_int(value.data());
dev->minor = parse_int(value);
else if (key == "DEVNAME")
strscpy(dev->devname, value.data(), sizeof(dev->devname));
strscpy(dev->devname, value.c_str(), sizeof(dev->devname));
else if (key == "PARTNAME")
strscpy(dev->partname, value.data(), sizeof(dev->devname));
strscpy(dev->partname, value.c_str(), sizeof(dev->devname));
return true;
});

View File

@@ -67,26 +67,26 @@ static bool patch_rc_scripts(const char *src_path, const char *tmp_path, bool wr
xopenat(dest_fd, INIT_RC, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0), "we");
if (!dest_rc) return false;
LOGD("Patching " INIT_RC " in %s\n", src_path);
file_readline(src_rc, [&dest_rc](string_view line) -> bool {
file_readline(src_rc, [&dest_rc](Utf8CStr line) -> bool {
// Do not start vaultkeeper
if (str_contains(line, "start vaultkeeper")) {
if (line.sv().contains("start vaultkeeper")) {
LOGD("Remove vaultkeeper\n");
return true;
}
// Do not run flash_recovery
if (line.starts_with("service flash_recovery")) {
if (line.sv().starts_with("service flash_recovery")) {
LOGD("Remove flash_recovery\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=")) {
if (line.sv().starts_with("on property:persist.sys.zygote.early=")) {
LOGD("Invalidate persist.sys.zygote.early\n");
fprintf(dest_rc.get(), "on property:persist.sys.zygote.early.xxxxx=true\n");
return true;
}
// Else just write the line
fprintf(dest_rc.get(), "%s", line.data());
fprintf(dest_rc.get(), "%s", line.c_str());
return true;
});
@@ -108,24 +108,26 @@ static bool patch_rc_scripts(const char *src_path, const char *tmp_path, bool wr
// 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;
owned_fd src_rc = xopenat(src_fd, name.data(), O_RDONLY | O_CLOEXEC, 0);
{
auto name = std::string_view(entry->d_name);
if (!name.starts_with("init.zygote") || !name.ends_with(".rc")) continue;
}
owned_fd src_rc = xopenat(src_fd, entry->d_name, O_RDONLY | O_CLOEXEC, 0);
if (src_rc < 0) continue;
if (writable) unlinkat(src_fd, name.data(), 0);
if (writable) unlinkat(src_fd, entry->d_name, 0);
auto dest_rc = xopen_file(
xopenat(dest_fd, name.data(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0), "we");
xopenat(dest_fd, entry->d_name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0), "we");
if (!dest_rc) continue;
LOGD("Patching %s in %s\n", name.data(), src_path);
file_readline(src_rc, [&dest_rc, &tmp_path](string_view line) -> bool {
if (line.starts_with("service zygote ")) {
LOGD("Patching %s in %s\n", entry->d_name, src_path);
file_readline(src_rc, [&dest_rc, &tmp_path](Utf8CStr line) -> bool {
if (line.sv().starts_with("service zygote ")) {
LOGD("Inject zygote restart\n");
fprintf(dest_rc.get(), "%s", line.data());
fprintf(dest_rc.get(), "%s", line.c_str());
fprintf(dest_rc.get(),
" onrestart exec " MAGISK_PROC_CON " 0 0 -- %s/magisk --zygote-restart\n", tmp_path);
return true;
}
fprintf(dest_rc.get(), "%s", line.data());
fprintf(dest_rc.get(), "%s", line.c_str());
return true;
});
fclone_attr(src_rc, fileno(dest_rc.get()));
@@ -184,7 +186,7 @@ static void load_overlay_rc(const char *overlay) {
char buf[NAME_MAX + 2];
buf[0] = '/';
for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (!str_ends(entry->d_name, ".rc")) {
if (!string_view(entry->d_name).ends_with(".rc")) {
continue;
}
strscpy(buf + 1, entry->d_name, sizeof(buf) - 1);
@@ -407,7 +409,7 @@ static void unxz_init(const char *init_xz, const char *init) {
unlink(init_xz);
}
rust::Utf8CStr backup_init() {
Utf8CStr backup_init() {
if (access("/.backup/init.xz", F_OK) == 0)
unxz_init("/.backup/init.xz", "/.backup/init");
return "/.backup/init";

View File

@@ -30,7 +30,6 @@ pub mod ffi {
include!("policy.hpp");
include!("../base/include/base.hpp");
#[namespace = "rust"]
#[cxx_name = "Utf8CStr"]
type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>;

View File

@@ -97,7 +97,7 @@ SePolicy SePolicy::from_data(rust::Slice<const uint8_t> data) noexcept {
return {std::make_unique<sepol_impl>(db)};
}
SePolicy SePolicy::from_file(::rust::Utf8CStr file) noexcept {
SePolicy SePolicy::from_file(::Utf8CStr file) noexcept {
LOGD("Load policy from: %.*s\n", static_cast<int>(file.size()), file.data());
policy_file_t pf;
@@ -235,7 +235,7 @@ static int vec_write(void *v, const char *buf, int len) {
return len;
}
bool SePolicy::to_file(::rust::Utf8CStr file) const noexcept {
bool SePolicy::to_file(::Utf8CStr file) const noexcept {
// No partial writes are allowed to /sys/fs/selinux/load, thus the reason why we
// first dump everything into memory, then directly call write system call
vector<char> out;