diff --git a/native/src/base/base.cpp b/native/src/base/base.cpp index 9a280718d..4fa1380f0 100644 --- a/native/src/base/base.cpp +++ b/native/src/base/base.cpp @@ -1,13 +1,9 @@ -#include #include #include #include #include -#include -#include +#include #include -#include -#include #include #include @@ -18,6 +14,12 @@ using namespace std; #define __call_bypassing_fortify(fn) (&fn) #endif +#ifdef __LP64__ +static_assert(BLKGETSIZE64 == 0x80081272); +#else +static_assert(BLKGETSIZE64 == 0x80041272); +#endif + // Override libc++ new implementation to optimize final build size void* operator new(std::size_t s) { return std::malloc(s); } @@ -29,32 +31,19 @@ void* operator new[](std::size_t s, const std::nothrow_t&) noexcept { return std void operator delete(void *p, const std::nothrow_t&) noexcept { std::free(p); } void operator delete[](void *p, const std::nothrow_t&) noexcept { std::free(p); } -bool byte_view::contains(byte_view pattern) const { - return _buf != nullptr && memmem(_buf, _sz, pattern._buf, pattern._sz) != nullptr; -} - -bool byte_view::operator==(byte_view rhs) const { - return _sz == rhs._sz && memcmp(_buf, rhs._buf, _sz) == 0; -} - -void byte_data::swap(byte_data &o) { - std::swap(_buf, o._buf); - std::swap(_sz, o._sz); -} - rust::Vec byte_data::patch(byte_view from, byte_view to) const { rust::Vec v; - if (_buf == nullptr) + if (ptr == nullptr) return v; - auto p = _buf; - auto eof = _buf + _sz; + auto p = ptr; + auto eof = ptr + sz; while (p < eof) { p = static_cast(memmem(p, eof - p, from.data(), from.size())); if (p == nullptr) return v; memset(p, 0, from.size()); memcpy(p, to.data(), to.size()); - v.push_back(p - _buf); + v.push_back(p - ptr); p += from.size(); } return v; @@ -374,30 +363,34 @@ sFILE make_file(FILE *fp) { mmap_data::mmap_data(const char *name, bool rw) { auto slice = rust::map_file(name, rw); if (!slice.empty()) { - _buf = slice.data(); - _sz = slice.size(); + this->ptr = slice.data(); + this->sz = slice.size(); } } mmap_data::mmap_data(int dirfd, const char *name, bool rw) { auto slice = rust::map_file_at(dirfd, name, rw); if (!slice.empty()) { - _buf = slice.data(); - _sz = slice.size(); + this->ptr = slice.data(); + this->sz = slice.size(); } } mmap_data::mmap_data(int fd, size_t sz, bool rw) { auto slice = rust::map_fd(fd, sz, rw); if (!slice.empty()) { - _buf = slice.data(); - _sz = slice.size(); + this->ptr = slice.data(); + this->sz = slice.size(); } } mmap_data::~mmap_data() { - if (_buf) - munmap(_buf, _sz); + if (ptr) munmap(ptr, sz); +} + +void mmap_data::swap(mmap_data &o) { + std::swap(ptr, o.ptr); + std::swap(sz, o.sz); } string resolve_preinit_dir(const char *base_dir) { @@ -420,7 +413,7 @@ 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); -Utf8CStr::Utf8CStr(const char *s, size_t len) { +Utf8CStr::Utf8CStr(const char *s, size_t len) : repr{} { cxx$utf8str$new(this, s, len); } diff --git a/native/src/base/cxx_extern.rs b/native/src/base/cxx_extern.rs index d7a833c49..ebe4e0470 100644 --- a/native/src/base/cxx_extern.rs +++ b/native/src/base/cxx_extern.rs @@ -4,14 +4,14 @@ 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 std::os::fd::{BorrowedFd, FromRawFd, RawFd}; use crate::ffi::{FnBoolStr, FnBoolStrStr}; use crate::files::map_file_at; pub(crate) use crate::xwrap::*; use crate::{ - BufReadExt, Directory, LoggedResult, ResultExt, Utf8CStr, clone_attr, cstr, fclone_attr, - map_fd, map_file, slice_from_ptr, + BufReadExt, ResultExt, Utf8CStr, clone_attr, cstr, fclone_attr, map_fd, map_file, + slice_from_ptr, }; use cfg_if::cfg_if; use libc::{c_char, mode_t}; @@ -52,14 +52,6 @@ unsafe extern "C" fn rm_rf_for_cxx(path: *const c_char) -> bool { } } -#[unsafe(no_mangle)] -unsafe extern "C" fn frm_rf(fd: OwnedFd) -> bool { - fn inner(fd: OwnedFd) -> LoggedResult<()> { - Directory::try_from(fd)?.remove_all() - } - inner(fd).is_ok() -} - pub(crate) fn map_file_for_cxx(path: &Utf8CStr, rw: bool) -> &'static mut [u8] { map_file(path, rw).log().unwrap_or(&mut []) } diff --git a/native/src/base/files.hpp b/native/src/base/files.hpp deleted file mode 100644 index 29c84ea64..000000000 --- a/native/src/base/files.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "base-rs.hpp" - -struct mmap_data : public byte_data { - static_assert((sizeof(void *) == 8 && BLKGETSIZE64 == 0x80081272) || - (sizeof(void *) == 4 && BLKGETSIZE64 == 0x80041272)); - ALLOW_MOVE_ONLY(mmap_data) - - mmap_data() = default; - explicit mmap_data(const char *name, bool rw = false); - mmap_data(int dirfd, const char *name, bool rw = false); - mmap_data(int fd, size_t sz, bool rw = false); - ~mmap_data(); -}; - -extern "C" { - -int mkdirs(const char *path, mode_t mode); -ssize_t canonical_path(const char * __restrict__ path, char * __restrict__ buf, size_t bufsiz); -bool rm_rf(const char *path); -bool frm_rf(int dirfd); -bool cp_afc(const char *src, const char *dest); -bool mv_path(const char *src, const char *dest); -bool link_path(const char *src, const char *dest); -bool clone_attr(const char *src, const char *dest); -bool fclone_attr(int src, int dest); - -} // extern "C" - -std::string full_read(int fd); -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 -template -void parse_prop_file(const char *file, Functor &&fn) { - parse_prop_file_rs(file, [&](rust::Str key, rust::Str val) -> bool { - // 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(key.data()) + key.size()) = '\0'; - *(const_cast(val.data()) + val.size()) = '\0'; - return fn(Utf8CStr(key.data(), key.size() + 1), Utf8CStr(val.data(), val.size() + 1)); - }); -} - -using sFILE = std::unique_ptr; -using sDIR = std::unique_ptr; -sDIR make_dir(DIR *dp); -sFILE make_file(FILE *fp); - -static inline sDIR open_dir(const char *path) { - return make_dir(opendir(path)); -} - -static inline sDIR xopen_dir(const char *path) { - return make_dir(xopendir(path)); -} - -static inline sDIR xopen_dir(int dirfd) { - return make_dir(xfdopendir(dirfd)); -} - -static inline sFILE open_file(const char *path, const char *mode) { - return make_file(fopen(path, mode)); -} - -static inline sFILE xopen_file(const char *path, const char *mode) { - return make_file(xfopen(path, mode)); -} - -static inline sFILE xopen_file(int fd, const char *mode) { - return make_file(xfdopen(fd, mode)); -} diff --git a/native/src/base/include/base.hpp b/native/src/base/include/base.hpp index 649676c99..30fe40f76 100644 --- a/native/src/base/include/base.hpp +++ b/native/src/base/include/base.hpp @@ -1,10 +1,353 @@ #pragma once -#include "../xwrap.hpp" -#include "../misc.hpp" -#include "../base-rs.hpp" -#include "../files.hpp" -#include "../logging.hpp" +#include +#include +#include +#include +#include -using rust::xpipe2; -using kv_pairs = std::vector>; +#include + +void LOGD(const char *fmt, ...) __printflike(1, 2); +void LOGI(const char *fmt, ...) __printflike(1, 2); +void LOGW(const char *fmt, ...) __printflike(1, 2); +void LOGE(const char *fmt, ...) __printflike(1, 2); +#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s\n", ##args, errno, std::strerror(errno)) + +extern "C" { + +// xwraps + +FILE *xfopen(const char *pathname, const char *mode); +FILE *xfdopen(int fd, const char *mode); +int xopen(const char *pathname, int flags, mode_t mode = 0); +int xopenat(int dirfd, const char *pathname, int flags, mode_t mode = 0); +ssize_t xwrite(int fd, const void *buf, size_t count); +ssize_t xread(int fd, void *buf, size_t count); +ssize_t xxread(int fd, void *buf, size_t count); +int xsetns(int fd, int nstype); +int xunshare(int flags); +DIR *xopendir(const char *name); +DIR *xfdopendir(int fd); +dirent *xreaddir(DIR *dirp); +pid_t xsetsid(); +int xfstat(int fd, struct stat *buf); +int xdup2(int oldfd, int newfd); +ssize_t xreadlinkat( + int dirfd, const char * __restrict__ pathname, char * __restrict__ buf, size_t bufsiz); +int xsymlink(const char *target, const char *linkpath); +int xmount(const char *source, const char *target, + const char *filesystemtype, unsigned long mountflags, + const void *data); +int xumount2(const char *target, int flags); +int xrename(const char *oldpath, const char *newpath); +int xmkdir(const char *pathname, mode_t mode); +int xmkdirs(const char *pathname, mode_t mode); +ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count); +pid_t xfork(); +ssize_t xrealpath(const char * __restrict__ path, char * __restrict__ buf, size_t bufsiz); +int xmknod(const char * pathname, mode_t mode, dev_t dev); + +// Utils + +int mkdirs(const char *path, mode_t mode); +ssize_t canonical_path(const char * __restrict__ path, char * __restrict__ buf, size_t bufsiz); +bool rm_rf(const char *path); +bool cp_afc(const char *src, const char *dest); +bool mv_path(const char *src, const char *dest); +bool link_path(const char *src, const char *dest); +bool clone_attr(const char *src, const char *dest); +bool fclone_attr(int src, int dest); + +} // extern "C" + +#define DISALLOW_COPY_AND_MOVE(clazz) \ +clazz(const clazz&) = delete; \ +clazz(clazz &&) = delete; + +#define ALLOW_MOVE_ONLY(clazz) \ +clazz(const clazz&) = delete; \ +clazz(clazz &&o) : clazz() { swap(o); } \ +clazz& operator=(clazz &&o) { swap(o); return *this; } + +struct Utf8CStr; + +class mutex_guard { + DISALLOW_COPY_AND_MOVE(mutex_guard) +public: + explicit mutex_guard(pthread_mutex_t &m): mutex(&m) { + pthread_mutex_lock(mutex); + } + void unlock() { + pthread_mutex_unlock(mutex); + mutex = nullptr; + } + ~mutex_guard() { + if (mutex) pthread_mutex_unlock(mutex); + } +private: + pthread_mutex_t *mutex; +}; + +template +class run_finally { + DISALLOW_COPY_AND_MOVE(run_finally) +public: + explicit run_finally(Func &&fn) : fn(std::move(fn)) {} + ~run_finally() { fn(); } +private: + Func fn; +}; + +template +static void default_new(T *&p) { p = new T(); } + +template +static void default_new(std::unique_ptr &p) { p.reset(new T()); } + +struct StringCmp { + using is_transparent = void; + bool operator()(std::string_view a, std::string_view b) const { return a < b; } +}; + +using ByteSlice = rust::Slice; +using MutByteSlice = rust::Slice; + +// Interchangeable as `&[u8]` in Rust +struct byte_view { + byte_view() : ptr(nullptr), sz(0) {} + byte_view(const void *buf, size_t sz) : ptr((uint8_t *) buf), sz(sz) {} + + // byte_view, or any of its subclasses, can be copied as byte_view + byte_view(const byte_view &o) : ptr(o.ptr), sz(o.sz) {} + + // Transparent conversion to Rust slice + byte_view(const ByteSlice o) : byte_view(o.data(), o.size()) {} + operator ByteSlice() const { return {ptr, sz}; } + + // String as bytes, including null terminator + byte_view(const char *s) : byte_view(s, strlen(s) + 1) {} + + const uint8_t *data() const { return ptr; } + size_t size() const { return sz; } + +protected: + uint8_t *ptr; + size_t sz; +}; + +// Interchangeable as `&mut [u8]` in Rust +struct byte_data : public byte_view { + byte_data() = default; + byte_data(void *buf, size_t sz) : byte_view(buf, sz) {} + + // byte_data, or any of its subclasses, can be copied as byte_data + byte_data(const byte_data &o) : byte_data(o.ptr, o.sz) {} + + // Transparent conversion to Rust slice + byte_data(const MutByteSlice o) : byte_data(o.data(), o.size()) {} + operator MutByteSlice() const { return {ptr, sz}; } + + using byte_view::data; + uint8_t *data() const { return ptr; } + + rust::Vec patch(byte_view from, byte_view to) const; +}; + +struct mmap_data : public byte_data { + ALLOW_MOVE_ONLY(mmap_data) + + mmap_data() = default; + explicit mmap_data(const char *name, bool rw = false); + mmap_data(int dirfd, const char *name, bool rw = false); + mmap_data(int fd, size_t sz, bool rw = false); + ~mmap_data(); +private: + void swap(mmap_data &o); +}; + + +struct owned_fd { + ALLOW_MOVE_ONLY(owned_fd) + + owned_fd() : fd(-1) {} + owned_fd(int fd) : fd(fd) {} + ~owned_fd() { close(fd); fd = -1; } + + operator int() { return fd; } + int release() { int f = fd; fd = -1; return f; } + void swap(owned_fd &owned) { std::swap(fd, owned.fd); } + +private: + int fd; +}; + +rust::Vec mut_u8_patch(MutByteSlice buf, ByteSlice from, ByteSlice to); + +uint32_t parse_uint32_hex(std::string_view s); +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 std::string rtrim(std::string &&s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { + return !std::isspace(ch) && ch != '\0'; + }).base(), s.end()); + return std::move(s); +} + +int fork_dont_care(); +int fork_no_orphan(); +void init_argv0(int argc, char **argv); +void set_nice_name(Utf8CStr name); +int switch_mnt_ns(int pid); +std::string &replace_all(std::string &str, std::string_view from, std::string_view to); +std::vector split(std::string_view s, std::string_view delims); + +// Similar to vsnprintf, but the return value is the written number of bytes +__printflike(3, 0) int vssprintf(char *dest, size_t size, const char *fmt, va_list ap); +// Similar to snprintf, but the return value is the written number of bytes +__printflike(3, 4) int ssprintf(char *dest, size_t size, const char *fmt, ...); +// This is not actually the strscpy from the Linux kernel. +// Silently truncates, and returns the number of bytes written. +extern "C" size_t strscpy(char *dest, const char *src, size_t size); + +// Ban usage of unsafe cstring functions +#define vsnprintf __use_vssprintf_instead__ +#define snprintf __use_ssprintf_instead__ +#define strlcpy __use_strscpy_instead__ + +struct exec_t { + bool err = false; + int fd = -2; + void (*pre_exec)() = nullptr; + int (*fork)() = xfork; + const char **argv = nullptr; +}; + +int exec_command(exec_t &exec); +template +int exec_command(exec_t &exec, Args &&...args) { + const char *argv[] = {args..., nullptr}; + exec.argv = argv; + return exec_command(exec); +} +int exec_command_sync(exec_t &exec); +template +int exec_command_sync(exec_t &exec, Args &&...args) { + const char *argv[] = {args..., nullptr}; + exec.argv = argv; + return exec_command_sync(exec); +} +template +int exec_command_sync(Args &&...args) { + exec_t exec; + return exec_command_sync(exec, args...); +} +template +void exec_command_async(Args &&...args) { + const char *argv[] = {args..., nullptr}; + exec_t exec { + .fork = fork_dont_care, + .argv = argv, + }; + exec_command(exec); +} + +template +constexpr auto operator+(T e) noexcept -> + std::enable_if_t::value, std::underlying_type_t> { + return static_cast>(e); +} + +std::string full_read(int fd); +std::string full_read(const char *filename); +void write_zero(int fd, size_t size); +std::string resolve_preinit_dir(const char *base_dir); + +using sFILE = std::unique_ptr; +using sDIR = std::unique_ptr; +sDIR make_dir(DIR *dp); +sFILE make_file(FILE *fp); + +static inline sDIR open_dir(const char *path) { + return make_dir(opendir(path)); +} + +static inline sDIR xopen_dir(const char *path) { + return make_dir(xopendir(path)); +} + +static inline sDIR xopen_dir(int dirfd) { + return make_dir(xfdopendir(dirfd)); +} + +static inline sFILE open_file(const char *path, const char *mode) { + return make_file(fopen(path, mode)); +} + +static inline sFILE xopen_file(const char *path, const char *mode) { + return make_file(xfopen(path, mode)); +} + +static inline sFILE xopen_file(int fd, const char *mode) { + return make_file(xfdopen(fd, mode)); +} + +// Bindings to &Utf8CStr in Rust +struct Utf8CStr { + const char *data() const; + size_t length() const; + Utf8CStr(const char *s, size_t len); + + Utf8CStr() : Utf8CStr("", 1) {}; + Utf8CStr(const Utf8CStr &o) = default; + Utf8CStr(const char *s) : Utf8CStr(s, strlen(s) + 1) {}; + Utf8CStr(std::string s) : Utf8CStr(s.data(), s.length() + 1) {}; + const char *c_str() const { return this->data(); } + size_t size() const { return this->length(); } + bool empty() const { return this->length() == 0 ; } + 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 +#pragma clang diagnostic ignored "-Wunused-private-field" + std::array repr; +#pragma clang diagnostic pop +}; + +// Bindings for std::function to be callable from Rust +using CxxFnBoolStrStr = std::function; +struct FnBoolStrStr : public CxxFnBoolStrStr { + using CxxFnBoolStrStr::function; + bool call(rust::Str a, rust::Str b) const { + return operator()(a, b); + } +}; +using CxxFnBoolStr = std::function; +struct FnBoolStr : public CxxFnBoolStr { + using CxxFnBoolStr::function; + bool call(Utf8CStr s) const { + return operator()(s); + } +}; + +#include "../base-rs.hpp" + +// Functor = function +template +void parse_prop_file(const char *file, Functor &&fn) { + parse_prop_file_rs(file, [&](rust::Str key, rust::Str val) -> bool { + // 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(key.data()) + key.size()) = '\0'; + *(const_cast(val.data()) + val.size()) = '\0'; + return fn(Utf8CStr(key.data(), key.size() + 1), Utf8CStr(val.data(), val.size() + 1)); + }); +} diff --git a/native/src/base/lib.rs b/native/src/base/lib.rs index af626b793..d96d54639 100644 --- a/native/src/base/lib.rs +++ b/native/src/base/lib.rs @@ -39,7 +39,7 @@ mod ffi { } unsafe extern "C++" { - include!("misc.hpp"); + include!("base.hpp"); #[cxx_name = "Utf8CStr"] type Utf8CStrRef<'a> = &'a crate::cstr::Utf8CStr; @@ -62,11 +62,11 @@ mod ffi { fn parse_prop_file_rs(name: Utf8CStrRef, f: &FnBoolStrStr); #[cxx_name = "file_readline"] fn file_readline_for_cxx(fd: i32, f: &FnBoolStr); + fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32; } #[namespace = "rust"] extern "Rust" { - fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32; #[cxx_name = "map_file"] fn map_file_for_cxx(path: Utf8CStrRef, rw: bool) -> &'static mut [u8]; #[cxx_name = "map_file_at"] diff --git a/native/src/base/logging.hpp b/native/src/base/logging.hpp deleted file mode 100644 index 405864ce3..000000000 --- a/native/src/base/logging.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include -#include - -void LOGD(const char *fmt, ...) __printflike(1, 2); -void LOGI(const char *fmt, ...) __printflike(1, 2); -void LOGW(const char *fmt, ...) __printflike(1, 2); -void LOGE(const char *fmt, ...) __printflike(1, 2); -#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s\n", ##args, errno, std::strerror(errno)) diff --git a/native/src/base/misc.hpp b/native/src/base/misc.hpp deleted file mode 100644 index 1e11d5da0..000000000 --- a/native/src/base/misc.hpp +++ /dev/null @@ -1,250 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "xwrap.hpp" - -#define DISALLOW_COPY_AND_MOVE(clazz) \ -clazz(const clazz&) = delete; \ -clazz(clazz &&) = delete; - -#define ALLOW_MOVE_ONLY(clazz) \ -clazz(const clazz&) = delete; \ -clazz(clazz &&o) : clazz() { swap(o); } \ -clazz& operator=(clazz &&o) { swap(o); return *this; } - -struct Utf8CStr; - -class mutex_guard { - DISALLOW_COPY_AND_MOVE(mutex_guard) -public: - explicit mutex_guard(pthread_mutex_t &m): mutex(&m) { - pthread_mutex_lock(mutex); - } - void unlock() { - pthread_mutex_unlock(mutex); - mutex = nullptr; - } - ~mutex_guard() { - if (mutex) pthread_mutex_unlock(mutex); - } -private: - pthread_mutex_t *mutex; -}; - -template -class run_finally { - DISALLOW_COPY_AND_MOVE(run_finally) -public: - explicit run_finally(Func &&fn) : fn(std::move(fn)) {} - ~run_finally() { fn(); } -private: - Func fn; -}; - -template -static void default_new(T *&p) { p = new T(); } - -template -static void default_new(std::unique_ptr &p) { p.reset(new T()); } - -struct StringCmp { - using is_transparent = void; - bool operator()(std::string_view a, std::string_view b) const { return a < b; } -}; - -struct heap_data; - -using ByteSlice = rust::Slice; -using MutByteSlice = rust::Slice; - -// Interchangeable as `&[u8]` in Rust -struct byte_view { - byte_view() : _buf(nullptr), _sz(0) {} - byte_view(const void *buf, size_t sz) : _buf((uint8_t *) buf), _sz(sz) {} - - // byte_view, or any of its subclasses, can be copied as byte_view - byte_view(const byte_view &o) : _buf(o._buf), _sz(o._sz) {} - - // Transparent conversion to Rust slice - byte_view(const ByteSlice o) : byte_view(o.data(), o.size()) {} - operator ByteSlice() const { return {_buf, _sz}; } - - // String as bytes, including null terminator - byte_view(const char *s) : byte_view(s, strlen(s) + 1) {} - - const uint8_t *data() const { return _buf; } - size_t size() const { return _sz; } - bool contains(byte_view pattern) const; - bool operator==(byte_view rhs) const; - -protected: - uint8_t *_buf; - size_t _sz; -}; - -// Interchangeable as `&mut [u8]` in Rust -struct byte_data : public byte_view { - byte_data() = default; - byte_data(void *buf, size_t sz) : byte_view(buf, sz) {} - - // byte_data, or any of its subclasses, can be copied as byte_data - byte_data(const byte_data &o) : byte_data(o._buf, o._sz) {} - - // Transparent conversion to Rust slice - byte_data(const MutByteSlice o) : byte_data(o.data(), o.size()) {} - operator MutByteSlice() const { return {_buf, _sz}; } - - using byte_view::data; - uint8_t *data() const { return _buf; } - - void swap(byte_data &o); - rust::Vec patch(byte_view from, byte_view to) const; -}; - -struct heap_data : public byte_data { - ALLOW_MOVE_ONLY(heap_data) - - heap_data() = default; - explicit heap_data(size_t sz) : byte_data(calloc(sz, 1), sz) {} - ~heap_data() { free(_buf); } -}; - -struct owned_fd { - ALLOW_MOVE_ONLY(owned_fd) - - owned_fd() : fd(-1) {} - owned_fd(int fd) : fd(fd) {} - ~owned_fd() { close(fd); fd = -1; } - - operator int() { return fd; } - int release() { int f = fd; fd = -1; return f; } - void swap(owned_fd &owned) { std::swap(fd, owned.fd); } - -private: - int fd; -}; - -rust::Vec mut_u8_patch(MutByteSlice buf, ByteSlice from, ByteSlice to); - -uint32_t parse_uint32_hex(std::string_view s); -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 std::string rtrim(std::string &&s) { - s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { - return !std::isspace(ch) && ch != '\0'; - }).base(), s.end()); - return std::move(s); -} - -int fork_dont_care(); -int fork_no_orphan(); -void init_argv0(int argc, char **argv); -void set_nice_name(Utf8CStr name); -int switch_mnt_ns(int pid); -std::string &replace_all(std::string &str, std::string_view from, std::string_view to); -std::vector split(std::string_view s, std::string_view delims); - -// Similar to vsnprintf, but the return value is the written number of bytes -__printflike(3, 0) int vssprintf(char *dest, size_t size, const char *fmt, va_list ap); -// Similar to snprintf, but the return value is the written number of bytes -__printflike(3, 4) int ssprintf(char *dest, size_t size, const char *fmt, ...); -// This is not actually the strscpy from the Linux kernel. -// Silently truncates, and returns the number of bytes written. -extern "C" size_t strscpy(char *dest, const char *src, size_t size); - -// Ban usage of unsafe cstring functions -#define vsnprintf __use_vssprintf_instead__ -#define snprintf __use_ssprintf_instead__ -#define strlcpy __use_strscpy_instead__ - -struct exec_t { - bool err = false; - int fd = -2; - void (*pre_exec)() = nullptr; - int (*fork)() = xfork; - const char **argv = nullptr; -}; - -int exec_command(exec_t &exec); -template -int exec_command(exec_t &exec, Args &&...args) { - const char *argv[] = {args..., nullptr}; - exec.argv = argv; - return exec_command(exec); -} -int exec_command_sync(exec_t &exec); -template -int exec_command_sync(exec_t &exec, Args &&...args) { - const char *argv[] = {args..., nullptr}; - exec.argv = argv; - return exec_command_sync(exec); -} -template -int exec_command_sync(Args &&...args) { - exec_t exec; - return exec_command_sync(exec, args...); -} -template -void exec_command_async(Args &&...args) { - const char *argv[] = {args..., nullptr}; - exec_t exec { - .fork = fork_dont_care, - .argv = argv, - }; - exec_command(exec); -} - -template -constexpr auto operator+(T e) noexcept -> - std::enable_if_t::value, std::underlying_type_t> { - return static_cast>(e); -} - -struct Utf8CStr { - const char *data() const; - size_t length() const; - Utf8CStr(const char *s, size_t len); - - Utf8CStr() : Utf8CStr("", 1) {}; - Utf8CStr(const Utf8CStr &o) = default; - Utf8CStr(const char *s) : Utf8CStr(s, strlen(s) + 1) {}; - Utf8CStr(std::string s) : Utf8CStr(s.data(), s.length() + 1) {}; - const char *c_str() const { return this->data(); } - size_t size() const { return this->length(); } - bool empty() const { return this->length() == 0 ; } - 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 -#pragma clang diagnostic ignored "-Wunused-private-field" - std::array repr; -#pragma clang diagnostic pop -}; - -// Bindings for std::function to be callable from Rust - -using CxxFnBoolStrStr = std::function; -struct FnBoolStrStr : public CxxFnBoolStrStr { - using CxxFnBoolStrStr::function; - bool call(rust::Str a, rust::Str b) const { - return operator()(a, b); - } -}; -using CxxFnBoolStr = std::function; -struct FnBoolStr : public CxxFnBoolStr { - using CxxFnBoolStr::function; - bool call(Utf8CStr s) const { - return operator()(s); - } -}; diff --git a/native/src/base/xwrap.hpp b/native/src/base/xwrap.hpp deleted file mode 100644 index d037386ab..000000000 --- a/native/src/base/xwrap.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -extern "C" { - -FILE *xfopen(const char *pathname, const char *mode); -FILE *xfdopen(int fd, const char *mode); -int xopen(const char *pathname, int flags, mode_t mode = 0); -int xopenat(int dirfd, const char *pathname, int flags, mode_t mode = 0); -ssize_t xwrite(int fd, const void *buf, size_t count); -ssize_t xread(int fd, void *buf, size_t count); -ssize_t xxread(int fd, void *buf, size_t count); -int xsetns(int fd, int nstype); -int xunshare(int flags); -DIR *xopendir(const char *name); -DIR *xfdopendir(int fd); -dirent *xreaddir(DIR *dirp); -pid_t xsetsid(); -int xstat(const char *pathname, struct stat *buf); -int xfstat(int fd, struct stat *buf); -int xdup2(int oldfd, int newfd); -ssize_t xreadlink(const char * __restrict__ pathname, char * __restrict__ buf, size_t bufsiz); -ssize_t xreadlinkat( - int dirfd, const char * __restrict__ pathname, char * __restrict__ buf, size_t bufsiz); -int xsymlink(const char *target, const char *linkpath); -int xmount(const char *source, const char *target, - const char *filesystemtype, unsigned long mountflags, - const void *data); -int xumount2(const char *target, int flags); -int xrename(const char *oldpath, const char *newpath); -int xmkdir(const char *pathname, mode_t mode); -int xmkdirs(const char *pathname, mode_t mode); -ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count); -pid_t xfork(); -ssize_t xrealpath(const char * __restrict__ path, char * __restrict__ buf, size_t bufsiz); -int xmknod(const char * pathname, mode_t mode, dev_t dev); - -} // extern "C" diff --git a/native/src/base/xwrap.rs b/native/src/base/xwrap.rs index 496a5868a..042d4d8b7 100644 --- a/native/src/base/xwrap.rs +++ b/native/src/base/xwrap.rs @@ -35,21 +35,6 @@ unsafe extern "C" fn xrealpath(path: *const c_char, buf: *mut u8, bufsz: usize) } } -#[unsafe(no_mangle)] -unsafe extern "C" fn xreadlink(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize { - unsafe { - match Utf8CStr::from_ptr(path) { - Ok(path) => { - let mut buf = cstr::buf::wrap_ptr(buf, bufsz); - path.read_link(&mut buf) - .log() - .map_or(-1, |_| buf.len() as isize) - } - Err(_) => -1, - } - } -} - #[unsafe(no_mangle)] unsafe extern "C" fn xreadlinkat( dirfd: RawFd, @@ -201,16 +186,6 @@ extern "C" fn xsetsid() -> i32 { } } -#[unsafe(no_mangle)] -unsafe extern "C" fn xstat(path: *const c_char, buf: *mut libc::stat) -> i32 { - unsafe { - libc::stat(path, buf) - .into_os_result("stat", ptr_to_str(path), None) - .log() - .unwrap_or(-1) - } -} - #[unsafe(no_mangle)] unsafe extern "C" fn xfstat(fd: RawFd, buf: *mut libc::stat) -> i32 { unsafe { diff --git a/native/src/core/su/su.cpp b/native/src/core/su/su.cpp index 4a3532968..70dfb5194 100644 --- a/native/src/core/su/su.cpp +++ b/native/src/core/su/su.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include diff --git a/native/src/core/zygisk/entry.cpp b/native/src/core/zygisk/entry.cpp index 54b5b5364..e0e8fddc7 100644 --- a/native/src/core/zygisk/entry.cpp +++ b/native/src/core/zygisk/entry.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include diff --git a/native/src/init/init.hpp b/native/src/init/init.hpp index 952cff159..9262e4229 100644 --- a/native/src/init/init.hpp +++ b/native/src/init/init.hpp @@ -12,6 +12,8 @@ #include #include +using kv_pairs = std::vector>; + #include "init-rs.hpp" int magisk_proxy_main(int, char *argv[]);