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

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