mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-12-17 06:11:48 +00:00
Restructure the native module
Consolidate all code into the src folder
This commit is contained in:
32
native/src/base/Android.mk
Normal file
32
native/src/base/Android.mk
Normal file
@@ -0,0 +1,32 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
# Magisk project-wide common code
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libbase
|
||||
LOCAL_C_INCLUDES := src/include $(LOCAL_PATH)/include out/generated
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
|
||||
LOCAL_EXPORT_STATIC_LIBRARIES := libcxx
|
||||
LOCAL_STATIC_LIBRARIES := libcxx
|
||||
LOCAL_SRC_FILES := \
|
||||
new.cpp \
|
||||
files.cpp \
|
||||
misc.cpp \
|
||||
selinux.cpp \
|
||||
logging.cpp \
|
||||
xwrap.cpp \
|
||||
stream.cpp \
|
||||
../external/cxx-rs/src/cxx.cc
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# All static executables should link with libcompat
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libcompat
|
||||
# Workaround "hacky" libc.a missing symbols
|
||||
# To build Magisk with vanilla NDK, comment out the next line
|
||||
LOCAL_SRC_FILES := compat/compat.cpp
|
||||
# Fix static variables' ctor/dtor when using LTO
|
||||
# See: https://github.com/android/ndk/issues/1461
|
||||
LOCAL_EXPORT_LDLIBS := -static -T src/lto_fix.lds
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
10
native/src/base/Cargo.toml
Normal file
10
native/src/base/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "base"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
cxx = "1.0.69"
|
||||
153
native/src/base/compat/compat.cpp
Normal file
153
native/src/base/compat/compat.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
// This file implements all missing symbols that should exist in normal API 21
|
||||
// libc.a but missing in our extremely lean libc.a replacements.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <mntent.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
// Original source: https://github.com/freebsd/freebsd/blob/master/contrib/file/src/getline.c
|
||||
// License: BSD, full copyright notice please check original source
|
||||
|
||||
ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) {
|
||||
char *ptr, *eptr;
|
||||
|
||||
if (*buf == nullptr || *bufsiz == 0) {
|
||||
*bufsiz = BUFSIZ;
|
||||
if ((*buf = (char *) malloc(*bufsiz)) == nullptr)
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (ptr = *buf, eptr = *buf + *bufsiz;;) {
|
||||
int c = fgetc(fp);
|
||||
if (c == -1) {
|
||||
if (feof(fp))
|
||||
return ptr == *buf ? -1 : ptr - *buf;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
*ptr++ = c;
|
||||
if (c == delimiter) {
|
||||
*ptr = '\0';
|
||||
return ptr - *buf;
|
||||
}
|
||||
if (ptr + 2 >= eptr) {
|
||||
char *nbuf;
|
||||
size_t nbufsiz = *bufsiz * 2;
|
||||
ssize_t d = ptr - *buf;
|
||||
if ((nbuf = (char *) realloc(*buf, nbufsiz)) == nullptr)
|
||||
return -1;
|
||||
*buf = nbuf;
|
||||
*bufsiz = nbufsiz;
|
||||
eptr = nbuf + nbufsiz;
|
||||
ptr = nbuf + d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t getline(char **buf, size_t *bufsiz, FILE *fp) {
|
||||
return getdelim(buf, bufsiz, '\n', fp);
|
||||
}
|
||||
|
||||
FILE *setmntent(const char *path, const char *mode) {
|
||||
return fopen(path, mode);
|
||||
}
|
||||
|
||||
int endmntent(FILE *fp) {
|
||||
if (fp != nullptr) {
|
||||
fclose(fp);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Missing system call wrappers
|
||||
|
||||
int setns(int fd, int nstype) {
|
||||
return syscall(__NR_setns, fd, nstype);
|
||||
}
|
||||
|
||||
int unshare(int flags) {
|
||||
return syscall(__NR_unshare, flags);
|
||||
}
|
||||
|
||||
int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) {
|
||||
return syscall(__NR_accept4, sockfd, addr, addrlen, flags);
|
||||
}
|
||||
|
||||
int dup3(int oldfd, int newfd, int flags) {
|
||||
return syscall(__NR_dup3, oldfd, newfd, flags);
|
||||
}
|
||||
|
||||
ssize_t readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) {
|
||||
return syscall(__NR_readlinkat, dirfd, pathname, buf, bufsiz);
|
||||
}
|
||||
|
||||
int symlinkat(const char *target, int newdirfd, const char *linkpath) {
|
||||
return syscall(__NR_symlinkat, target, newdirfd, linkpath);
|
||||
}
|
||||
|
||||
int linkat(int olddirfd, const char *oldpath,
|
||||
int newdirfd, const char *newpath, int flags) {
|
||||
return syscall(__NR_linkat, olddirfd, oldpath, newdirfd, newpath, flags);
|
||||
}
|
||||
|
||||
int inotify_init1(int flags) {
|
||||
return syscall(__NR_inotify_init1, flags);
|
||||
}
|
||||
|
||||
int faccessat(int dirfd, const char *pathname, int mode, int flags) {
|
||||
return syscall(__NR_faccessat, dirfd, pathname, mode, flags);
|
||||
}
|
||||
|
||||
int mkfifo(const char *path, mode_t mode) {
|
||||
return mknod(path, (mode & ~S_IFMT) | S_IFIFO, 0);
|
||||
}
|
||||
|
||||
#define SPLIT_64(v) (unsigned)((v) & 0xFFFFFFFF), (unsigned)((v) >> 32)
|
||||
|
||||
#if defined(__arm__)
|
||||
// Why the additional 0 is required: https://man7.org/linux/man-pages/man2/syscall.2.html
|
||||
int ftruncate64(int fd, off64_t length) {
|
||||
return syscall(__NR_ftruncate64, fd, 0, SPLIT_64(length));
|
||||
}
|
||||
#elif defined(__i386__)
|
||||
int ftruncate64(int fd, off64_t length) {
|
||||
return syscall(__NR_ftruncate64, fd, SPLIT_64(length));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(__LP64__)
|
||||
void android_set_abort_message(const char *) {}
|
||||
|
||||
// Original source: <android/legacy_signal_inlines.h>
|
||||
int sigaddset(sigset_t *set, int signum) {
|
||||
/* Signal numbers start at 1, but bit positions start at 0. */
|
||||
int bit = signum - 1;
|
||||
auto *local_set = (unsigned long *)set;
|
||||
if (set == nullptr || bit < 0 || bit >= (int)(8 * sizeof(sigset_t))) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
local_set[bit / LONG_BIT] |= 1UL << (bit % LONG_BIT);
|
||||
return 0;
|
||||
}
|
||||
int sigemptyset(sigset_t *set) {
|
||||
if (set == nullptr) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
memset(set, 0, sizeof(sigset_t));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "fortify.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
} // extern "C"
|
||||
110
native/src/base/compat/fortify.hpp
Normal file
110
native/src/base/compat/fortify.hpp
Normal file
@@ -0,0 +1,110 @@
|
||||
// Original source: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/fortify.cpp
|
||||
// License: AOSP, full copyright notice please check original source
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#undef _FORTIFY_SOURCE
|
||||
|
||||
extern void __vloge(const char* fmt, va_list ap);
|
||||
|
||||
static inline __noreturn __printflike(1, 2) void __fortify_fatal(const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
__vloge(fmt, args);
|
||||
va_end(args);
|
||||
abort();
|
||||
}
|
||||
static inline void __check_count(const char* fn, const char* identifier, size_t value) {
|
||||
if (__predict_false(value > SSIZE_MAX)) {
|
||||
__fortify_fatal("%s: %s %zu > SSIZE_MAX", fn, identifier, value);
|
||||
}
|
||||
}
|
||||
static inline void __check_buffer_access(const char* fn, const char* action,
|
||||
size_t claim, size_t actual) {
|
||||
if (__predict_false(claim > actual)) {
|
||||
__fortify_fatal("%s: prevented %zu-byte %s %zu-byte buffer", fn, claim, action, actual);
|
||||
}
|
||||
}
|
||||
char* __strcpy_chk(char* dst, const char* src, size_t dst_len) {
|
||||
// TODO: optimize so we don't scan src twice.
|
||||
size_t src_len = __builtin_strlen(src) + 1;
|
||||
__check_buffer_access("strcpy", "write into", src_len, dst_len);
|
||||
return __builtin_strcpy(dst, src);
|
||||
}
|
||||
size_t __strlcpy_chk(char* dst, const char* src,
|
||||
size_t supplied_size, size_t dst_len_from_compiler) {
|
||||
__check_buffer_access("strlcpy", "write into", supplied_size, dst_len_from_compiler);
|
||||
return __call_bypassing_fortify(strlcpy)(dst, src, supplied_size);
|
||||
}
|
||||
char* __strchr_chk(const char* p, int ch, size_t s_len) {
|
||||
for (;; ++p, s_len--) {
|
||||
if (__predict_false(s_len == 0)) {
|
||||
__fortify_fatal("strchr: prevented read past end of buffer");
|
||||
}
|
||||
if (*p == static_cast<char>(ch)) {
|
||||
return const_cast<char*>(p);
|
||||
}
|
||||
if (*p == '\0') {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
char* __strcat_chk(char* dst, const char* src, size_t dst_buf_size) {
|
||||
char* save = dst;
|
||||
size_t dst_len = __strlen_chk(dst, dst_buf_size);
|
||||
dst += dst_len;
|
||||
dst_buf_size -= dst_len;
|
||||
while ((*dst++ = *src++) != '\0') {
|
||||
dst_buf_size--;
|
||||
if (__predict_false(dst_buf_size == 0)) {
|
||||
__fortify_fatal("strcat: prevented write past end of %zu-byte buffer", dst_buf_size);
|
||||
}
|
||||
}
|
||||
return save;
|
||||
}
|
||||
size_t __strlen_chk(const char* s, size_t s_len) {
|
||||
// TODO: "prevented" here would be a lie because this strlen can run off the end.
|
||||
// strlen is too important to be expensive, so we wanted to be able to call the optimized
|
||||
// implementation, but I think we need to implement optimized assembler __strlen_chk routines.
|
||||
size_t ret = __builtin_strlen(s);
|
||||
if (__predict_false(ret >= s_len)) {
|
||||
__fortify_fatal("strlen: detected read past end of buffer");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
int __vsprintf_chk(char* dst, int /*flags*/,
|
||||
size_t dst_len_from_compiler, const char* format, va_list va) {
|
||||
// The compiler uses SIZE_MAX to mean "no idea", but our vsnprintf rejects sizes that large.
|
||||
int result = __call_bypassing_fortify(vsnprintf)(dst,
|
||||
dst_len_from_compiler == SIZE_MAX ? SSIZE_MAX : dst_len_from_compiler,
|
||||
format, va);
|
||||
// Try to catch failures after the fact...
|
||||
__check_buffer_access("vsprintf", "write into", result + 1, dst_len_from_compiler);
|
||||
return result;
|
||||
}
|
||||
mode_t __umask_chk(mode_t mode) {
|
||||
if (__predict_false((mode & 0777) != mode)) {
|
||||
__fortify_fatal("umask: called with invalid mask %o", mode);
|
||||
}
|
||||
return __umask_real(mode);
|
||||
}
|
||||
ssize_t __read_chk(int fd, void* buf, size_t count, size_t buf_size) {
|
||||
__check_count("read", "count", count);
|
||||
__check_buffer_access("read", "write into", count, buf_size);
|
||||
return __call_bypassing_fortify(read)(fd, buf, count);
|
||||
}
|
||||
static inline bool needs_mode(int flags) {
|
||||
return ((flags & O_CREAT) == O_CREAT) || ((flags & O_TMPFILE) == O_TMPFILE);
|
||||
}
|
||||
static inline int force_O_LARGEFILE(int flags) {
|
||||
return flags | O_LARGEFILE;
|
||||
}
|
||||
int __open_2(const char* pathname, int flags) {
|
||||
if (needs_mode(flags)) __fortify_fatal("open: called with O_CREAT/O_TMPFILE but no mode");
|
||||
return __openat_real(AT_FDCWD, pathname, force_O_LARGEFILE(flags), 0);
|
||||
}
|
||||
int __openat_2(int fd, const char* pathname, int flags) {
|
||||
if (needs_mode(flags)) __fortify_fatal("open: called with O_CREAT/O_TMPFILE but no mode");
|
||||
return __openat_real(fd, pathname, force_O_LARGEFILE(flags), 0);
|
||||
}
|
||||
531
native/src/base/files.cpp
Normal file
531
native/src/base/files.cpp
Normal file
@@ -0,0 +1,531 @@
|
||||
#include <sys/sendfile.h>
|
||||
#include <linux/fs.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include <base.hpp>
|
||||
#include <selinux.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
ssize_t fd_path(int fd, char *path, size_t size) {
|
||||
snprintf(path, size, "/proc/self/fd/%d", fd);
|
||||
return xreadlink(path, path, size);
|
||||
}
|
||||
|
||||
int fd_pathat(int dirfd, const char *name, char *path, size_t size) {
|
||||
if (fd_path(dirfd, path, size) < 0)
|
||||
return -1;
|
||||
auto len = strlen(path);
|
||||
path[len] = '/';
|
||||
strlcpy(path + len + 1, name, size - len - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mkdirs(string path, mode_t mode) {
|
||||
errno = 0;
|
||||
for (char *p = path.data() + 1; *p; ++p) {
|
||||
if (*p == '/') {
|
||||
*p = '\0';
|
||||
if (mkdir(path.data(), mode) == -1) {
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
}
|
||||
*p = '/';
|
||||
}
|
||||
}
|
||||
if (mkdir(path.data(), mode) == -1) {
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
static void post_order_walk(int dirfd, const Func &fn) {
|
||||
auto dir = xopen_dir(dirfd);
|
||||
if (!dir) return;
|
||||
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
if (entry->d_type == DT_DIR)
|
||||
post_order_walk(xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC), fn);
|
||||
fn(dirfd, entry);
|
||||
}
|
||||
}
|
||||
|
||||
enum walk_result {
|
||||
CONTINUE, SKIP, ABORT
|
||||
};
|
||||
|
||||
template <typename Func>
|
||||
static walk_result pre_order_walk(int dirfd, const Func &fn) {
|
||||
auto dir = xopen_dir(dirfd);
|
||||
if (!dir) {
|
||||
close(dirfd);
|
||||
return SKIP;
|
||||
}
|
||||
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
switch (fn(dirfd, entry)) {
|
||||
case CONTINUE:
|
||||
break;
|
||||
case SKIP:
|
||||
continue;
|
||||
case ABORT:
|
||||
return ABORT;
|
||||
}
|
||||
if (entry->d_type == DT_DIR) {
|
||||
int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
if (pre_order_walk(fd, fn) == ABORT)
|
||||
return ABORT;
|
||||
}
|
||||
}
|
||||
return CONTINUE;
|
||||
}
|
||||
|
||||
static void remove_at(int dirfd, struct dirent *entry) {
|
||||
unlinkat(dirfd, entry->d_name, entry->d_type == DT_DIR ? AT_REMOVEDIR : 0);
|
||||
}
|
||||
|
||||
void rm_rf(const char *path) {
|
||||
struct stat st;
|
||||
if (lstat(path, &st) < 0)
|
||||
return;
|
||||
if (S_ISDIR(st.st_mode))
|
||||
frm_rf(xopen(path, O_RDONLY | O_CLOEXEC));
|
||||
remove(path);
|
||||
}
|
||||
|
||||
void frm_rf(int dirfd) {
|
||||
post_order_walk(dirfd, remove_at);
|
||||
}
|
||||
|
||||
void mv_path(const char *src, const char *dest) {
|
||||
file_attr attr;
|
||||
getattr(src, &attr);
|
||||
if (S_ISDIR(attr.st.st_mode)) {
|
||||
if (access(dest, F_OK) != 0) {
|
||||
xmkdirs(dest, 0);
|
||||
setattr(dest, &attr);
|
||||
}
|
||||
mv_dir(xopen(src, O_RDONLY | O_CLOEXEC), xopen(dest, O_RDONLY | O_CLOEXEC));
|
||||
} else{
|
||||
xrename(src, dest);
|
||||
}
|
||||
rmdir(src);
|
||||
}
|
||||
|
||||
void mv_dir(int src, int dest) {
|
||||
auto dir = xopen_dir(src);
|
||||
run_finally f([=]{ close(dest); });
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
switch (entry->d_type) {
|
||||
case DT_DIR:
|
||||
if (xfaccessat(dest, entry->d_name) == 0) {
|
||||
// Destination folder exists, needs recursive move
|
||||
int newsrc = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
int newdest = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
mv_dir(newsrc, newdest);
|
||||
unlinkat(src, entry->d_name, AT_REMOVEDIR);
|
||||
break;
|
||||
}
|
||||
// Else fall through
|
||||
case DT_LNK:
|
||||
case DT_REG:
|
||||
renameat(src, entry->d_name, dest, entry->d_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cp_afc(const char *src, const char *dest) {
|
||||
file_attr a;
|
||||
getattr(src, &a);
|
||||
|
||||
if (S_ISDIR(a.st.st_mode)) {
|
||||
xmkdirs(dest, 0);
|
||||
clone_dir(xopen(src, O_RDONLY | O_CLOEXEC), xopen(dest, O_RDONLY | O_CLOEXEC));
|
||||
} else{
|
||||
unlink(dest);
|
||||
if (S_ISREG(a.st.st_mode)) {
|
||||
int sfd = xopen(src, O_RDONLY | O_CLOEXEC);
|
||||
int dfd = xopen(dest, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0);
|
||||
xsendfile(dfd, sfd, nullptr, a.st.st_size);
|
||||
close(sfd);
|
||||
close(dfd);
|
||||
} else if (S_ISLNK(a.st.st_mode)) {
|
||||
char buf[4096];
|
||||
xreadlink(src, buf, sizeof(buf));
|
||||
xsymlink(buf, dest);
|
||||
}
|
||||
}
|
||||
setattr(dest, &a);
|
||||
}
|
||||
|
||||
void clone_dir(int src, int dest) {
|
||||
auto dir = xopen_dir(src);
|
||||
run_finally f([&]{ close(dest); });
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
file_attr a;
|
||||
getattrat(src, entry->d_name, &a);
|
||||
switch (entry->d_type) {
|
||||
case DT_DIR: {
|
||||
xmkdirat(dest, entry->d_name, 0);
|
||||
setattrat(dest, entry->d_name, &a);
|
||||
int sfd = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
int dst = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
clone_dir(sfd, dst);
|
||||
break;
|
||||
}
|
||||
case DT_REG: {
|
||||
int sfd = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
int dfd = xopenat(dest, entry->d_name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0);
|
||||
xsendfile(dfd, sfd, nullptr, a.st.st_size);
|
||||
fsetattr(dfd, &a);
|
||||
close(dfd);
|
||||
close(sfd);
|
||||
break;
|
||||
}
|
||||
case DT_LNK: {
|
||||
char buf[4096];
|
||||
xreadlinkat(src, entry->d_name, buf, sizeof(buf));
|
||||
xsymlinkat(buf, dest, entry->d_name);
|
||||
setattrat(dest, entry->d_name, &a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void link_path(const char *src, const char *dest) {
|
||||
link_dir(xopen(src, O_RDONLY | O_CLOEXEC), xopen(dest, O_RDONLY | O_CLOEXEC));
|
||||
}
|
||||
|
||||
void link_dir(int src, int dest) {
|
||||
auto dir = xopen_dir(src);
|
||||
run_finally f([&]{ close(dest); });
|
||||
for (dirent *entry; (entry = xreaddir(dir.get()));) {
|
||||
if (entry->d_type == DT_DIR) {
|
||||
file_attr a;
|
||||
getattrat(src, entry->d_name, &a);
|
||||
xmkdirat(dest, entry->d_name, 0);
|
||||
setattrat(dest, entry->d_name, &a);
|
||||
int sfd = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
int dfd = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
link_dir(sfd, dfd);
|
||||
} else {
|
||||
xlinkat(src, entry->d_name, dest, entry->d_name, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int getattr(const char *path, file_attr *a) {
|
||||
if (xlstat(path, &a->st) == -1)
|
||||
return -1;
|
||||
char *con;
|
||||
if (lgetfilecon(path, &con) == -1)
|
||||
return -1;
|
||||
strcpy(a->con, con);
|
||||
freecon(con);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getattrat(int dirfd, const char *name, file_attr *a) {
|
||||
char path[4096];
|
||||
fd_pathat(dirfd, name, path, sizeof(path));
|
||||
return getattr(path, a);
|
||||
}
|
||||
|
||||
int fgetattr(int fd, file_attr *a) {
|
||||
if (xfstat(fd, &a->st) < 0)
|
||||
return -1;
|
||||
char *con;
|
||||
if (fgetfilecon(fd, &con) < 0)
|
||||
return -1;
|
||||
strcpy(a->con, con);
|
||||
freecon(con);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int setattr(const char *path, file_attr *a) {
|
||||
if (chmod(path, a->st.st_mode & 0777) < 0)
|
||||
return -1;
|
||||
if (chown(path, a->st.st_uid, a->st.st_gid) < 0)
|
||||
return -1;
|
||||
if (a->con[0] && lsetfilecon(path, a->con) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int setattrat(int dirfd, const char *name, file_attr *a) {
|
||||
char path[4096];
|
||||
fd_pathat(dirfd, name, path, sizeof(path));
|
||||
return setattr(path, a);
|
||||
}
|
||||
|
||||
int fsetattr(int fd, file_attr *a) {
|
||||
if (fchmod(fd, a->st.st_mode & 0777) < 0)
|
||||
return -1;
|
||||
if (fchown(fd, a->st.st_uid, a->st.st_gid) < 0)
|
||||
return -1;
|
||||
if (a->con[0] && fsetfilecon(fd, a->con) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void clone_attr(const char *src, const char *dest) {
|
||||
file_attr a;
|
||||
getattr(src, &a);
|
||||
setattr(dest, &a);
|
||||
}
|
||||
|
||||
void fclone_attr(int src, int dest) {
|
||||
file_attr a;
|
||||
fgetattr(src, &a);
|
||||
fsetattr(dest, &a);
|
||||
}
|
||||
|
||||
void full_read(int fd, string &str) {
|
||||
char buf[4096];
|
||||
for (ssize_t len; (len = xread(fd, buf, sizeof(buf))) > 0;)
|
||||
str.insert(str.end(), buf, buf + len);
|
||||
}
|
||||
|
||||
void full_read(const char *filename, string &str) {
|
||||
if (int fd = xopen(filename, O_RDONLY | O_CLOEXEC); fd >= 0) {
|
||||
full_read(fd, str);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
string full_read(int fd) {
|
||||
string str;
|
||||
full_read(fd, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
string full_read(const char *filename) {
|
||||
string str;
|
||||
full_read(filename, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
void write_zero(int fd, size_t size) {
|
||||
char buf[4096] = {0};
|
||||
size_t len;
|
||||
while (size > 0) {
|
||||
len = sizeof(buf) > size ? size : sizeof(buf);
|
||||
write(fd, buf, len);
|
||||
size -= len;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Original source: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/mntent.cpp
|
||||
// License: AOSP, full copyright notice please check original source
|
||||
static struct mntent *compat_getmntent_r(FILE *fp, struct mntent *e, char *buf, int buf_len) {
|
||||
memset(e, 0, sizeof(*e));
|
||||
while (fgets(buf, buf_len, fp) != nullptr) {
|
||||
// Entries look like "proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0".
|
||||
// That is: mnt_fsname mnt_dir mnt_type mnt_opts 0 0.
|
||||
int fsname0, fsname1, dir0, dir1, type0, type1, opts0, opts1;
|
||||
if (sscanf(buf, " %n%*s%n %n%*s%n %n%*s%n %n%*s%n %d %d",
|
||||
&fsname0, &fsname1, &dir0, &dir1, &type0, &type1, &opts0, &opts1,
|
||||
&e->mnt_freq, &e->mnt_passno) == 2) {
|
||||
e->mnt_fsname = &buf[fsname0];
|
||||
buf[fsname1] = '\0';
|
||||
e->mnt_dir = &buf[dir0];
|
||||
buf[dir1] = '\0';
|
||||
e->mnt_type = &buf[type0];
|
||||
buf[type1] = '\0';
|
||||
e->mnt_opts = &buf[opts0];
|
||||
buf[opts1] = '\0';
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void parse_mnt(const char *file, const function<bool(mntent*)> &fn) {
|
||||
auto fp = sFILE(setmntent(file, "re"), endmntent);
|
||||
if (fp) {
|
||||
mntent mentry{};
|
||||
char buf[4096];
|
||||
// getmntent_r from system's libc.so is broken on old platform
|
||||
// use the compat one instead
|
||||
while (compat_getmntent_r(fp.get(), &mentry, buf, sizeof(buf))) {
|
||||
if (!fn(&mentry))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void backup_folder(const char *dir, vector<raw_file> &files) {
|
||||
char path[PATH_MAX];
|
||||
xrealpath(dir, path);
|
||||
int len = strlen(path);
|
||||
pre_order_walk(xopen(dir, O_RDONLY), [&](int dfd, dirent *entry) -> walk_result {
|
||||
int fd = xopenat(dfd, entry->d_name, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return SKIP;
|
||||
run_finally f([&]{ close(fd); });
|
||||
if (fd_path(fd, path, sizeof(path)) < 0)
|
||||
return SKIP;
|
||||
raw_file file;
|
||||
file.path = path + len + 1;
|
||||
if (fgetattr(fd, &file.attr) < 0)
|
||||
return SKIP;
|
||||
if (entry->d_type == DT_REG) {
|
||||
file.content = full_read(fd);
|
||||
} else if (entry->d_type == DT_LNK) {
|
||||
xreadlinkat(dfd, entry->d_name, path, sizeof(path));
|
||||
file.content = path;
|
||||
}
|
||||
files.emplace_back(std::move(file));
|
||||
return CONTINUE;
|
||||
});
|
||||
}
|
||||
|
||||
void restore_folder(const char *dir, vector<raw_file> &files) {
|
||||
string base(dir);
|
||||
// Pre-order means folders will always be first
|
||||
for (raw_file &file : files) {
|
||||
string path = base + "/" + file.path;
|
||||
if (S_ISDIR(file.attr.st.st_mode)) {
|
||||
mkdirs(path, 0);
|
||||
} else if (S_ISREG(file.attr.st.st_mode)) {
|
||||
if (auto fp = xopen_file(path.data(), "we"))
|
||||
fwrite(file.content.data(), 1, file.content.size(), fp.get());
|
||||
} else if (S_ISLNK(file.attr.st.st_mode)) {
|
||||
symlink(file.content.data(), path.data());
|
||||
}
|
||||
setattr(path.data(), &file.attr);
|
||||
}
|
||||
}
|
||||
|
||||
sDIR make_dir(DIR *dp) {
|
||||
return sDIR(dp, [](DIR *dp){ return dp ? closedir(dp) : 1; });
|
||||
}
|
||||
|
||||
sFILE make_file(FILE *fp) {
|
||||
return sFILE(fp, [](FILE *fp){ return fp ? fclose(fp) : 1; });
|
||||
}
|
||||
|
||||
int byte_data::patch(bool log, str_pairs list) {
|
||||
if (buf == nullptr)
|
||||
return 0;
|
||||
int count = 0;
|
||||
for (uint8_t *p = buf, *eof = buf + sz; p < eof; ++p) {
|
||||
for (auto [from, to] : list) {
|
||||
if (memcmp(p, from.data(), from.length() + 1) == 0) {
|
||||
if (log) LOGD("Replace [%s] -> [%s]\n", from.data(), to.data());
|
||||
memset(p, 0, from.length());
|
||||
memcpy(p, to.data(), to.length());
|
||||
++count;
|
||||
p += from.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool byte_data::contains(string_view pattern, bool log) const {
|
||||
if (buf == nullptr)
|
||||
return false;
|
||||
for (uint8_t *p = buf, *eof = buf + sz; p < eof; ++p) {
|
||||
if (memcmp(p, pattern.data(), pattern.length() + 1) == 0) {
|
||||
if (log) LOGD("Found pattern [%s]\n", pattern.data());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void byte_data::swap(byte_data &o) {
|
||||
std::swap(buf, o.buf);
|
||||
std::swap(sz, o.sz);
|
||||
}
|
||||
|
||||
mmap_data::mmap_data(const char *name, bool rw) {
|
||||
int fd = xopen(name, (rw ? O_RDWR : O_RDONLY) | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return;
|
||||
struct stat st;
|
||||
if (fstat(fd, &st))
|
||||
return;
|
||||
if (S_ISBLK(st.st_mode)) {
|
||||
uint64_t size;
|
||||
ioctl(fd, BLKGETSIZE64, &size);
|
||||
sz = size;
|
||||
} else {
|
||||
sz = st.st_size;
|
||||
}
|
||||
void *b = sz > 0
|
||||
? xmmap(nullptr, sz, PROT_READ | PROT_WRITE, rw ? MAP_SHARED : MAP_PRIVATE, fd, 0)
|
||||
: nullptr;
|
||||
close(fd);
|
||||
buf = static_cast<uint8_t *>(b);
|
||||
}
|
||||
|
||||
string find_apk_path(const char *pkg) {
|
||||
char buf[PATH_MAX];
|
||||
pre_order_walk(xopen("/data/app", O_RDONLY), [&](int dfd, dirent *entry) -> walk_result {
|
||||
if (entry->d_type != DT_DIR)
|
||||
return SKIP;
|
||||
size_t len = strlen(pkg);
|
||||
if (strncmp(entry->d_name, pkg, len) == 0 && entry->d_name[len] == '-') {
|
||||
fd_pathat(dfd, entry->d_name, buf, sizeof(buf));
|
||||
return ABORT;
|
||||
} else if (strncmp(entry->d_name, "~~", 2) == 0) {
|
||||
return CONTINUE;
|
||||
} else return SKIP;
|
||||
});
|
||||
string path(buf);
|
||||
return path.append("/base.apk");
|
||||
}
|
||||
123
native/src/base/files.hpp
Normal file
123
native/src/base/files.hpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <mntent.h>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "xwrap.hpp"
|
||||
|
||||
template <typename T>
|
||||
static inline T align_to(T v, int a) {
|
||||
static_assert(std::is_integral<T>::value);
|
||||
return (v + a - 1) / a * a;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T align_padding(T v, int a) {
|
||||
return align_to(v, a) - v;
|
||||
}
|
||||
|
||||
struct file_attr {
|
||||
struct stat st;
|
||||
char con[128];
|
||||
};
|
||||
|
||||
struct byte_data {
|
||||
using str_pairs = std::initializer_list<std::pair<std::string_view, std::string_view>>;
|
||||
|
||||
uint8_t *buf = nullptr;
|
||||
size_t sz = 0;
|
||||
|
||||
int patch(str_pairs list) { return patch(true, list); }
|
||||
int patch(bool log, str_pairs list);
|
||||
bool contains(std::string_view pattern, bool log = true) const;
|
||||
protected:
|
||||
void swap(byte_data &o);
|
||||
};
|
||||
|
||||
struct raw_file {
|
||||
std::string path;
|
||||
file_attr attr;
|
||||
std::string content;
|
||||
|
||||
raw_file() : attr{} {}
|
||||
raw_file(const raw_file&) = delete;
|
||||
raw_file(raw_file &&o) : path(std::move(o.path)), attr(o.attr), content(std::move(o.content)) {}
|
||||
};
|
||||
|
||||
struct mmap_data : public byte_data {
|
||||
mmap_data() = default;
|
||||
mmap_data(const mmap_data&) = delete;
|
||||
mmap_data(mmap_data &&o) { swap(o); }
|
||||
mmap_data(const char *name, bool rw = false);
|
||||
~mmap_data() { if (buf) munmap(buf, sz); }
|
||||
mmap_data& operator=(mmap_data &&other) { swap(other); return *this; }
|
||||
};
|
||||
|
||||
ssize_t fd_path(int fd, char *path, size_t size);
|
||||
int fd_pathat(int dirfd, const char *name, char *path, size_t size);
|
||||
int mkdirs(std::string path, mode_t mode);
|
||||
void rm_rf(const char *path);
|
||||
void mv_path(const char *src, const char *dest);
|
||||
void mv_dir(int src, int dest);
|
||||
void cp_afc(const char *src, const char *dest);
|
||||
void link_path(const char *src, const char *dest);
|
||||
void link_dir(int src, int dest);
|
||||
int getattr(const char *path, file_attr *a);
|
||||
int getattrat(int dirfd, const char *name, file_attr *a);
|
||||
int fgetattr(int fd, file_attr *a);
|
||||
int setattr(const char *path, file_attr *a);
|
||||
int setattrat(int dirfd, const char *name, file_attr *a);
|
||||
int fsetattr(int fd, file_attr *a);
|
||||
void fclone_attr(int src, int dest);
|
||||
void clone_attr(const char *src, const char *dest);
|
||||
void full_read(int fd, std::string &str);
|
||||
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);
|
||||
void frm_rf(int dirfd);
|
||||
void clone_dir(int src, int dest);
|
||||
void parse_mnt(const char *file, const std::function<bool(mntent*)> &fn);
|
||||
void backup_folder(const char *dir, std::vector<raw_file> &files);
|
||||
void restore_folder(const char *dir, std::vector<raw_file> &files);
|
||||
std::string find_apk_path(const char *pkg);
|
||||
|
||||
using sFILE = std::unique_ptr<FILE, decltype(&fclose)>;
|
||||
using sDIR = std::unique_ptr<DIR, decltype(&closedir)>;
|
||||
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));
|
||||
}
|
||||
8
native/src/base/include/base.hpp
Normal file
8
native/src/base/include/base.hpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../missing.hpp"
|
||||
#include "../xwrap.hpp"
|
||||
#include "../files.hpp"
|
||||
#include "../misc.hpp"
|
||||
#include "../logging.hpp"
|
||||
#include <base-rs.hpp>
|
||||
43
native/src/base/include/selinux.hpp
Normal file
43
native/src/base/include/selinux.hpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
// selinuxfs paths
|
||||
#define SELINUX_MNT "/sys/fs/selinux"
|
||||
#define SELINUX_ENFORCE SELINUX_MNT "/enforce"
|
||||
#define SELINUX_POLICY SELINUX_MNT "/policy"
|
||||
#define SELINUX_LOAD SELINUX_MNT "/load"
|
||||
#define SELINUX_CONTEXT SELINUX_MNT "/context"
|
||||
#define SELINUX_VERSION SELINUX_MNT "/policyvers"
|
||||
|
||||
// sepolicy paths
|
||||
#define PLAT_POLICY_DIR "/system/etc/selinux/"
|
||||
#define VEND_POLICY_DIR "/vendor/etc/selinux/"
|
||||
#define PROD_POLICY_DIR "/product/etc/selinux/"
|
||||
#define ODM_POLICY_DIR "/odm/etc/selinux/"
|
||||
#define SYSEXT_POLICY_DIR "/system_ext/etc/selinux/"
|
||||
#define SPLIT_PLAT_CIL PLAT_POLICY_DIR "plat_sepolicy.cil"
|
||||
|
||||
// Unconstrained domain the daemon and root processes run in
|
||||
#define SEPOL_PROC_DOMAIN "magisk"
|
||||
// Highly constrained domain, sole purpose is to connect to daemon
|
||||
#define SEPOL_CLIENT_DOMAIN "magisk_client"
|
||||
// Unconstrained file type that anyone can access
|
||||
#define SEPOL_FILE_TYPE "magisk_file"
|
||||
// Special file type to allow clients to transit to client domain automatically
|
||||
#define SEPOL_EXEC_TYPE "magisk_exec"
|
||||
|
||||
extern void (*freecon)(char *con);
|
||||
extern int (*setcon)(const char *con);
|
||||
extern int (*getfilecon)(const char *path, char **con);
|
||||
extern int (*lgetfilecon)(const char *path, char **con);
|
||||
extern int (*fgetfilecon)(int fd, char **con);
|
||||
extern int (*setfilecon)(const char *path, const char *con);
|
||||
extern int (*lsetfilecon)(const char *path, const char *con);
|
||||
extern int (*fsetfilecon)(int fd, const char *con);
|
||||
void getfilecon_at(int dirfd, const char *name, char **con);
|
||||
void setfilecon_at(int dirfd, const char *name, const char *con);
|
||||
|
||||
bool selinux_enabled();
|
||||
void enable_selinux();
|
||||
void restorecon();
|
||||
void restore_tmpcon();
|
||||
void restore_databincon();
|
||||
133
native/src/base/include/stream.hpp
Normal file
133
native/src/base/include/stream.hpp
Normal file
@@ -0,0 +1,133 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/uio.h>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
|
||||
#include "../files.hpp"
|
||||
|
||||
class stream {
|
||||
public:
|
||||
virtual ssize_t read(void *buf, size_t len);
|
||||
virtual ssize_t readFully(void *buf, size_t len);
|
||||
virtual ssize_t readv(const iovec *iov, int iovcnt);
|
||||
virtual bool write(const void *buf, size_t len);
|
||||
virtual ssize_t writev(const iovec *iov, int iovcnt);
|
||||
virtual off_t seek(off_t off, int whence);
|
||||
virtual ~stream() = default;
|
||||
};
|
||||
|
||||
using stream_ptr = std::unique_ptr<stream>;
|
||||
|
||||
// Delegates all operations to base stream
|
||||
class filter_stream : public stream {
|
||||
public:
|
||||
filter_stream(stream_ptr &&base) : base(std::move(base)) {}
|
||||
|
||||
ssize_t read(void *buf, size_t len) override;
|
||||
bool write(const void *buf, size_t len) override;
|
||||
virtual bool write(const void *buf, size_t len, bool final);
|
||||
|
||||
// Seeking while filtering does not make sense
|
||||
off_t seek(off_t off, int whence) final { return stream::seek(off, whence); }
|
||||
|
||||
protected:
|
||||
stream_ptr base;
|
||||
};
|
||||
|
||||
using filter_strm_ptr = std::unique_ptr<filter_stream>;
|
||||
|
||||
// Buffered output stream, writing in chunks
|
||||
class chunk_out_stream : public filter_stream {
|
||||
public:
|
||||
chunk_out_stream(stream_ptr &&base, size_t buf_sz, size_t chunk_sz)
|
||||
: filter_stream(std::move(base)), chunk_sz(chunk_sz), buf_sz(buf_sz) {}
|
||||
|
||||
chunk_out_stream(stream_ptr &&base, size_t buf_sz = 4096)
|
||||
: chunk_out_stream(std::move(base), buf_sz, buf_sz) {}
|
||||
|
||||
~chunk_out_stream() override { delete[] _buf; }
|
||||
|
||||
// Reading does not make sense
|
||||
ssize_t read(void *buf, size_t len) final { return stream::read(buf, len); }
|
||||
bool write(const void *buf, size_t len) final;
|
||||
bool write(const void *buf, size_t len, bool final) final;
|
||||
|
||||
protected:
|
||||
// Classes inheriting this class has to call finalize() in its destructor
|
||||
void finalize();
|
||||
virtual bool write_chunk(const void *buf, size_t len, bool final) = 0;
|
||||
|
||||
size_t chunk_sz;
|
||||
|
||||
private:
|
||||
size_t buf_sz;
|
||||
size_t buf_off = 0;
|
||||
uint8_t *_buf = nullptr;
|
||||
};
|
||||
|
||||
// Byte stream that dynamically allocates memory
|
||||
class byte_stream : public stream {
|
||||
public:
|
||||
byte_stream(uint8_t *&buf, size_t &len);
|
||||
template <class Byte>
|
||||
byte_stream(Byte *&buf, size_t &len) : byte_stream(reinterpret_cast<uint8_t *&>(buf), len) {}
|
||||
|
||||
ssize_t read(void *buf, size_t len) override;
|
||||
bool write(const void *buf, size_t len) override;
|
||||
off_t seek(off_t off, int whence) override;
|
||||
|
||||
private:
|
||||
uint8_t *&_buf;
|
||||
size_t &_len;
|
||||
size_t _pos = 0;
|
||||
size_t _cap = 0;
|
||||
|
||||
void resize(size_t new_pos, bool zero = false);
|
||||
};
|
||||
|
||||
class file_stream : public stream {
|
||||
public:
|
||||
bool write(const void *buf, size_t len) final;
|
||||
protected:
|
||||
virtual ssize_t do_write(const void *buf, size_t len) = 0;
|
||||
};
|
||||
|
||||
// File stream but does not close the file descriptor at any time
|
||||
class fd_stream : public file_stream {
|
||||
public:
|
||||
fd_stream(int fd) : fd(fd) {}
|
||||
ssize_t read(void *buf, size_t len) override;
|
||||
ssize_t readv(const iovec *iov, int iovcnt) override;
|
||||
ssize_t writev(const iovec *iov, int iovcnt) override;
|
||||
off_t seek(off_t off, int whence) override;
|
||||
protected:
|
||||
ssize_t do_write(const void *buf, size_t len) override;
|
||||
private:
|
||||
int fd;
|
||||
};
|
||||
|
||||
/* ****************************************
|
||||
* Bridge between stream class and C stdio
|
||||
* ****************************************/
|
||||
|
||||
// sFILE -> stream_ptr
|
||||
class fp_stream final : public file_stream {
|
||||
public:
|
||||
fp_stream(FILE *fp = nullptr) : fp(fp, fclose) {}
|
||||
fp_stream(sFILE &&fp) : fp(std::move(fp)) {}
|
||||
ssize_t read(void *buf, size_t len) override;
|
||||
off_t seek(off_t off, int whence) override;
|
||||
protected:
|
||||
ssize_t do_write(const void *buf, size_t len) override;
|
||||
private:
|
||||
sFILE fp;
|
||||
};
|
||||
|
||||
// stream_ptr -> sFILE
|
||||
sFILE make_stream_fp(stream_ptr &&strm);
|
||||
|
||||
template <class T, class... Args>
|
||||
sFILE make_stream_fp(Args &&... args) {
|
||||
return make_stream_fp(stream_ptr(new T(std::forward<Args>(args)...)));
|
||||
}
|
||||
25
native/src/base/lib.rs
Normal file
25
native/src/base/lib.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
#![feature(format_args_nl)]
|
||||
|
||||
pub use logging::*;
|
||||
pub use misc::*;
|
||||
|
||||
mod logging;
|
||||
mod misc;
|
||||
|
||||
#[cxx::bridge]
|
||||
pub mod ffi {
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum LogLevel {
|
||||
Error,
|
||||
Warn,
|
||||
Info,
|
||||
Debug,
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
fn log_with_rs(level: LogLevel, msg: &str);
|
||||
fn exit_on_error(b: bool);
|
||||
fn set_log_level_state(level: LogLevel, enabled: bool);
|
||||
fn cmdline_logging();
|
||||
}
|
||||
}
|
||||
75
native/src/base/logging.cpp
Normal file
75
native/src/base/logging.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#include <flags.h>
|
||||
#include <base.hpp>
|
||||
|
||||
// Just need to include it somewhere
|
||||
#include <base-rs.cpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static int fmt_and_log_with_rs(LogLevel level, const char *fmt, va_list ap) {
|
||||
char buf[4096];
|
||||
int ret = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
log_with_rs(level, rust::Str(buf, ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int (*cpp_logger)(LogLevel level, const char *fmt, va_list ap) = fmt_and_log_with_rs;
|
||||
|
||||
// Used to override external C library logging
|
||||
extern "C" int magisk_log_print(int prio, const char *tag, const char *fmt, ...) {
|
||||
LogLevel level;
|
||||
switch (prio) {
|
||||
case ANDROID_LOG_DEBUG:
|
||||
level = LogLevel::Debug;
|
||||
break;
|
||||
case ANDROID_LOG_INFO:
|
||||
level = LogLevel::Info;
|
||||
break;
|
||||
case ANDROID_LOG_WARN:
|
||||
level = LogLevel::Warn;
|
||||
break;
|
||||
case ANDROID_LOG_ERROR:
|
||||
level = LogLevel::Error;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
char fmt_buf[4096];
|
||||
auto len = strlcpy(fmt_buf, tag, sizeof(fmt_buf));
|
||||
// Prevent format specifications in the tag
|
||||
std::replace(fmt_buf, fmt_buf + len, '%', '_');
|
||||
snprintf(fmt_buf + len, sizeof(fmt_buf) - len, ": %s", fmt);
|
||||
va_list argv;
|
||||
va_start(argv, fmt);
|
||||
int ret = cpp_logger(level, fmt_buf, argv);
|
||||
va_end(argv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define LOG_BODY(level) { \
|
||||
va_list argv; \
|
||||
va_start(argv, fmt); \
|
||||
cpp_logger(LogLevel::level, fmt, argv); \
|
||||
va_end(argv); \
|
||||
}
|
||||
|
||||
// LTO will optimize out the NOP function
|
||||
#if MAGISK_DEBUG
|
||||
void LOGD(const char *fmt, ...) { LOG_BODY(Debug) }
|
||||
#else
|
||||
void LOGD(const char *fmt, ...) {}
|
||||
#endif
|
||||
void LOGI(const char *fmt, ...) { LOG_BODY(Info) }
|
||||
void LOGW(const char *fmt, ...) { LOG_BODY(Warn) }
|
||||
void LOGE(const char *fmt, ...) { LOG_BODY(Error) }
|
||||
|
||||
// Export raw symbol to fortify compat
|
||||
extern "C" void __vloge(const char* fmt, va_list ap) {
|
||||
cpp_logger(LogLevel::Error, fmt, ap);
|
||||
}
|
||||
14
native/src/base/logging.hpp
Normal file
14
native/src/base/logging.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdarg>
|
||||
|
||||
#include <base-rs.hpp>
|
||||
|
||||
extern int (*cpp_logger)(LogLevel level, const char *fmt, va_list ap);
|
||||
|
||||
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))
|
||||
139
native/src/base/logging.rs
Normal file
139
native/src/base/logging.rs
Normal file
@@ -0,0 +1,139 @@
|
||||
use std::fmt::Arguments;
|
||||
use std::io::{stderr, stdout, Write};
|
||||
use std::process::exit;
|
||||
|
||||
use crate::ffi::LogLevel;
|
||||
|
||||
// Ugly hack to avoid using enum
|
||||
#[allow(non_snake_case, non_upper_case_globals)]
|
||||
mod LogFlag {
|
||||
pub const DisableError: u32 = 0x1;
|
||||
pub const DisableWarn: u32 = 0x2;
|
||||
pub const DisableInfo: u32 = 0x4;
|
||||
pub const DisableDebug: u32 = 0x8;
|
||||
pub const ExitOnError: u32 = 0x10;
|
||||
}
|
||||
|
||||
// We don't need to care about thread safety, because all
|
||||
// logger changes will only happen on the main thread.
|
||||
pub static mut LOGGER: Logger = Logger {
|
||||
fmt: |_, _| {},
|
||||
write: |_, _| {},
|
||||
flags: 0,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Logger {
|
||||
pub fmt: fn(level: LogLevel, args: Arguments),
|
||||
pub write: fn(level: LogLevel, msg: &[u8]),
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
pub fn exit_on_error(b: bool) {
|
||||
unsafe {
|
||||
if b {
|
||||
LOGGER.flags |= LogFlag::ExitOnError;
|
||||
} else {
|
||||
LOGGER.flags &= !LogFlag::ExitOnError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LogLevel {
|
||||
fn to_disable_flag(&self) -> u32 {
|
||||
match *self {
|
||||
LogLevel::Error => LogFlag::DisableError,
|
||||
LogLevel::Warn => LogFlag::DisableWarn,
|
||||
LogLevel::Info => LogFlag::DisableInfo,
|
||||
LogLevel::Debug => LogFlag::DisableDebug,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_log_level_state(level: LogLevel, enabled: bool) {
|
||||
let flag = level.to_disable_flag();
|
||||
unsafe {
|
||||
if enabled {
|
||||
LOGGER.flags &= !flag
|
||||
} else {
|
||||
LOGGER.flags |= flag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_with_rs(level: LogLevel, msg: &str) {
|
||||
let logger = unsafe { LOGGER };
|
||||
if (logger.flags & level.to_disable_flag()) != 0 {
|
||||
return;
|
||||
}
|
||||
(logger.write)(level, msg.as_bytes());
|
||||
if level == LogLevel::Error && (logger.flags & LogFlag::ExitOnError) != 0 {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_impl(level: LogLevel, args: Arguments) {
|
||||
let logger = unsafe { LOGGER };
|
||||
if (logger.flags & level.to_disable_flag()) != 0 {
|
||||
return;
|
||||
}
|
||||
(logger.fmt)(level, args);
|
||||
if level == LogLevel::Error && (logger.flags & LogFlag::ExitOnError) != 0 {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmdline_logging() {
|
||||
fn print(level: LogLevel, args: Arguments) {
|
||||
if level == LogLevel::Info {
|
||||
print!("{}", args);
|
||||
} else {
|
||||
eprint!("{}", args);
|
||||
}
|
||||
}
|
||||
|
||||
fn write(level: LogLevel, msg: &[u8]) {
|
||||
if level == LogLevel::Info {
|
||||
stdout().write_all(msg).ok();
|
||||
} else {
|
||||
stderr().write_all(msg).ok();
|
||||
}
|
||||
}
|
||||
|
||||
let logger = Logger {
|
||||
fmt: print,
|
||||
write,
|
||||
flags: LogFlag::ExitOnError,
|
||||
};
|
||||
unsafe {
|
||||
LOGGER = logger;
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
($($arg:tt)+) => ($crate::log_impl($crate::ffi::LogLevel::Error, format_args_nl!($($arg)+)))
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! warn {
|
||||
($($arg:tt)+) => ($crate::log_impl($crate::ffi::LogLevel::Warn, format_args_nl!($($arg)+)))
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
($($arg:tt)+) => ($crate::log_impl($crate::ffi::LogLevel::Info, format_args_nl!($($arg)+)))
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[macro_export]
|
||||
macro_rules! debug {
|
||||
($($arg:tt)+) => ($crate::log_impl($crate::ffi::LogLevel::Debug, format_args_nl!($($arg)+)))
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
#[macro_export]
|
||||
macro_rules! debug {
|
||||
($($arg:tt)+) => {};
|
||||
}
|
||||
209
native/src/base/misc.cpp
Normal file
209
native/src/base/misc.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#include <syscall.h>
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
#include <base.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int fork_dont_care() {
|
||||
if (int pid = xfork()) {
|
||||
waitpid(pid, nullptr, 0);
|
||||
return pid;
|
||||
} else if (xfork()) {
|
||||
exit(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fork_no_orphan() {
|
||||
int pid = xfork();
|
||||
if (pid)
|
||||
return pid;
|
||||
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||
if (getppid() == 1)
|
||||
exit(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gen_rand_str(char *buf, int len, bool varlen) {
|
||||
constexpr char ALPHANUM[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
static std::mt19937 gen([]{
|
||||
if (access("/dev/urandom", F_OK) != 0)
|
||||
mknod("/dev/urandom", 0600 | S_IFCHR, makedev(1, 9));
|
||||
int fd = xopen("/dev/urandom", O_RDONLY | O_CLOEXEC);
|
||||
unsigned seed;
|
||||
xxread(fd, &seed, sizeof(seed));
|
||||
return seed;
|
||||
}());
|
||||
std::uniform_int_distribution<int> dist(0, sizeof(ALPHANUM) - 2);
|
||||
if (varlen) {
|
||||
std::uniform_int_distribution<int> len_dist(len / 2, len);
|
||||
len = len_dist(gen);
|
||||
}
|
||||
for (int i = 0; i < len - 1; ++i)
|
||||
buf[i] = ALPHANUM[dist(gen)];
|
||||
buf[len - 1] = '\0';
|
||||
return len - 1;
|
||||
}
|
||||
|
||||
int exec_command(exec_t &exec) {
|
||||
int pipefd[] = {-1, -1};
|
||||
int outfd = -1;
|
||||
|
||||
if (exec.fd == -1) {
|
||||
if (xpipe2(pipefd, O_CLOEXEC) == -1)
|
||||
return -1;
|
||||
outfd = pipefd[1];
|
||||
} else if (exec.fd >= 0) {
|
||||
outfd = exec.fd;
|
||||
}
|
||||
|
||||
int pid = exec.fork();
|
||||
if (pid < 0) {
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
return -1;
|
||||
} else if (pid) {
|
||||
if (exec.fd == -1) {
|
||||
exec.fd = pipefd[0];
|
||||
close(pipefd[1]);
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
// Unblock all signals
|
||||
sigset_t set;
|
||||
sigfillset(&set);
|
||||
pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
|
||||
|
||||
if (outfd >= 0) {
|
||||
xdup2(outfd, STDOUT_FILENO);
|
||||
if (exec.err)
|
||||
xdup2(outfd, STDERR_FILENO);
|
||||
close(outfd);
|
||||
}
|
||||
|
||||
// Call the pre-exec callback
|
||||
if (exec.pre_exec)
|
||||
exec.pre_exec();
|
||||
|
||||
execve(exec.argv[0], (char **) exec.argv, environ);
|
||||
PLOGE("execve %s", exec.argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
int exec_command_sync(exec_t &exec) {
|
||||
int pid = exec_command(exec);
|
||||
if (pid < 0)
|
||||
return -1;
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
int new_daemon_thread(thread_entry entry, void *arg) {
|
||||
pthread_t thread;
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
return xpthread_create(&thread, &attr, entry, arg);
|
||||
}
|
||||
|
||||
static char *argv0;
|
||||
static size_t name_len;
|
||||
void init_argv0(int argc, char **argv) {
|
||||
argv0 = argv[0];
|
||||
name_len = (argv[argc - 1] - argv[0]) + strlen(argv[argc - 1]) + 1;
|
||||
}
|
||||
|
||||
void set_nice_name(const char *name) {
|
||||
memset(argv0, 0, name_len);
|
||||
strlcpy(argv0, name, name_len);
|
||||
prctl(PR_SET_NAME, name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bionic's atoi runs through strtol().
|
||||
* Use our own implementation for faster conversion.
|
||||
*/
|
||||
int parse_int(string_view s) {
|
||||
int val = 0;
|
||||
for (char c : s) {
|
||||
if (!c) break;
|
||||
if (c > '9' || c < '0')
|
||||
return -1;
|
||||
val = val * 10 + c - '0';
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
uint32_t binary_gcd(uint32_t u, uint32_t v) {
|
||||
if (u == 0) return v;
|
||||
if (v == 0) return u;
|
||||
auto shift = __builtin_ctz(u | v);
|
||||
u >>= __builtin_ctz(u);
|
||||
do {
|
||||
v >>= __builtin_ctz(v);
|
||||
if (u > v) {
|
||||
auto t = v;
|
||||
v = u;
|
||||
u = t;
|
||||
}
|
||||
v -= u;
|
||||
} while (v != 0);
|
||||
return u << shift;
|
||||
}
|
||||
|
||||
int switch_mnt_ns(int pid) {
|
||||
char mnt[32];
|
||||
snprintf(mnt, sizeof(mnt), "/proc/%d/ns/mnt", pid);
|
||||
if (access(mnt, R_OK) == -1) return 1; // Maybe process died..
|
||||
|
||||
int fd, ret;
|
||||
fd = xopen(mnt, O_RDONLY);
|
||||
if (fd < 0) return 1;
|
||||
// Switch to its namespace
|
||||
ret = xsetns(fd, 0);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
string &replace_all(string &str, string_view from, string_view to) {
|
||||
size_t pos = 0;
|
||||
while((pos = str.find(from, pos)) != string::npos) {
|
||||
str.replace(pos, from.length(), to);
|
||||
pos += to.length();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static auto split_impl(T s, T delims) {
|
||||
vector<std::decay_t<T>> result;
|
||||
size_t base = 0;
|
||||
size_t found;
|
||||
while (true) {
|
||||
found = s.find_first_of(delims, base);
|
||||
result.push_back(s.substr(base, found - base));
|
||||
if (found == string::npos)
|
||||
break;
|
||||
base = found + 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<string> split(const string &s, const string &delims) {
|
||||
return split_impl<const string&>(s, delims);
|
||||
}
|
||||
|
||||
vector<string_view> split_ro(string_view s, string_view delims) {
|
||||
return split_impl<string_view>(s, delims);
|
||||
}
|
||||
194
native/src/base/misc.hpp
Normal file
194
native/src/base/misc.hpp
Normal file
@@ -0,0 +1,194 @@
|
||||
#pragma once
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <bitset>
|
||||
|
||||
#define DISALLOW_COPY_AND_MOVE(clazz) \
|
||||
clazz(const clazz &) = delete; \
|
||||
clazz(clazz &&) = delete;
|
||||
|
||||
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 Func>
|
||||
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 <typename T>
|
||||
class reversed_container {
|
||||
public:
|
||||
reversed_container(T &base) : base(base) {}
|
||||
decltype(std::declval<T>().rbegin()) begin() { return base.rbegin(); }
|
||||
decltype(std::declval<T>().crbegin()) begin() const { return base.crbegin(); }
|
||||
decltype(std::declval<T>().crbegin()) cbegin() const { return base.crbegin(); }
|
||||
decltype(std::declval<T>().rend()) end() { return base.rend(); }
|
||||
decltype(std::declval<T>().crend()) end() const { return base.crend(); }
|
||||
decltype(std::declval<T>().crend()) cend() const { return base.crend(); }
|
||||
private:
|
||||
T &base;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
reversed_container<T> reversed(T &base) {
|
||||
return reversed_container<T>(base);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static inline void default_new(T *&p) { p = new T(); }
|
||||
|
||||
template<class T>
|
||||
static inline void default_new(std::unique_ptr<T> &p) { p.reset(new T()); }
|
||||
|
||||
template<typename T, typename Impl>
|
||||
class stateless_allocator {
|
||||
public:
|
||||
using value_type = T;
|
||||
T *allocate(size_t num) { return static_cast<T*>(Impl::allocate(sizeof(T) * num)); }
|
||||
void deallocate(T *ptr, size_t num) { Impl::deallocate(ptr, sizeof(T) * num); }
|
||||
stateless_allocator() = default;
|
||||
stateless_allocator(const stateless_allocator&) = default;
|
||||
stateless_allocator(stateless_allocator&&) = default;
|
||||
template <typename U>
|
||||
stateless_allocator(const stateless_allocator<U, Impl>&) {}
|
||||
bool operator==(const stateless_allocator&) { return true; }
|
||||
bool operator!=(const stateless_allocator&) { return false; }
|
||||
};
|
||||
|
||||
class dynamic_bitset_impl {
|
||||
public:
|
||||
using slot_type = unsigned long;
|
||||
constexpr static int slot_size = sizeof(slot_type) * 8;
|
||||
using slot_bits = std::bitset<slot_size>;
|
||||
|
||||
size_t slots() const { return slot_list.size(); }
|
||||
slot_type get_slot(size_t slot) const {
|
||||
return slot_list.size() > slot ? slot_list[slot].to_ulong() : 0ul;
|
||||
}
|
||||
void emplace_back(slot_type l) {
|
||||
slot_list.emplace_back(l);
|
||||
}
|
||||
protected:
|
||||
slot_bits::reference get(size_t pos) {
|
||||
size_t slot = pos / slot_size;
|
||||
size_t index = pos % slot_size;
|
||||
if (slot_list.size() <= slot) {
|
||||
slot_list.resize(slot + 1);
|
||||
}
|
||||
return slot_list[slot][index];
|
||||
}
|
||||
bool get(size_t pos) const {
|
||||
size_t slot = pos / slot_size;
|
||||
size_t index = pos % slot_size;
|
||||
return slot_list.size() > slot && slot_list[slot][index];
|
||||
}
|
||||
private:
|
||||
std::vector<slot_bits> slot_list;
|
||||
};
|
||||
|
||||
struct dynamic_bitset : public dynamic_bitset_impl {
|
||||
slot_bits::reference operator[] (size_t pos) { return get(pos); }
|
||||
bool operator[] (size_t pos) const { return get(pos); }
|
||||
};
|
||||
|
||||
struct StringCmp {
|
||||
using is_transparent = void;
|
||||
bool operator()(std::string_view a, std::string_view b) const { return a < b; }
|
||||
};
|
||||
|
||||
int parse_int(std::string_view s);
|
||||
|
||||
using thread_entry = void *(*)(void *);
|
||||
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';
|
||||
}).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(const char *name);
|
||||
uint32_t binary_gcd(uint32_t u, uint32_t v);
|
||||
int switch_mnt_ns(int pid);
|
||||
int gen_rand_str(char *buf, int len, bool varlen = true);
|
||||
std::string &replace_all(std::string &str, std::string_view from, std::string_view to);
|
||||
std::vector<std::string> split(const std::string &s, const std::string &delims);
|
||||
std::vector<std::string_view> split_ro(std::string_view, std::string_view delims);
|
||||
|
||||
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 <class ...Args>
|
||||
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 <class ...Args>
|
||||
int exec_command_sync(exec_t &exec, Args &&...args) {
|
||||
const char *argv[] = {args..., nullptr};
|
||||
exec.argv = argv;
|
||||
return exec_command_sync(exec);
|
||||
}
|
||||
template <class ...Args>
|
||||
int exec_command_sync(Args &&...args) {
|
||||
exec_t exec;
|
||||
return exec_command_sync(exec, args...);
|
||||
}
|
||||
template <class ...Args>
|
||||
void exec_command_async(Args &&...args) {
|
||||
const char *argv[] = {args..., nullptr};
|
||||
exec_t exec {
|
||||
.fork = fork_dont_care,
|
||||
.argv = argv,
|
||||
};
|
||||
exec_command(exec);
|
||||
}
|
||||
41
native/src/base/misc.rs
Normal file
41
native/src/base/misc.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use std::cmp::min;
|
||||
use std::fmt;
|
||||
use std::fmt::Arguments;
|
||||
|
||||
struct BufFmtWriter<'a> {
|
||||
buf: &'a mut [u8],
|
||||
used: usize,
|
||||
}
|
||||
|
||||
impl<'a> BufFmtWriter<'a> {
|
||||
fn new(buf: &'a mut [u8]) -> Self {
|
||||
BufFmtWriter { buf, used: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Write for BufFmtWriter<'a> {
|
||||
// The buffer should always be null terminated
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
if self.used >= self.buf.len() - 1 {
|
||||
// Silent truncate
|
||||
return Ok(());
|
||||
}
|
||||
let remain = &mut self.buf[self.used..];
|
||||
let s_bytes = s.as_bytes();
|
||||
let copied = min(s_bytes.len(), remain.len() - 1);
|
||||
remain[..copied].copy_from_slice(&s_bytes[..copied]);
|
||||
self.used += copied;
|
||||
self.buf[self.used] = b'\0';
|
||||
// Silent truncate
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmt_to_buf(buf: &mut [u8], args: Arguments) -> usize {
|
||||
let mut w = BufFmtWriter::new(buf);
|
||||
if let Ok(()) = fmt::write(&mut w, args) {
|
||||
w.used
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
26
native/src/base/missing.hpp
Normal file
26
native/src/base/missing.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
|
||||
static inline int sigtimedwait(const sigset_t* set, siginfo_t* info, const timespec* timeout) {
|
||||
union {
|
||||
sigset_t set;
|
||||
sigset_t set64;
|
||||
} s{};
|
||||
s.set = *set;
|
||||
return syscall(__NR_rt_sigtimedwait, &s.set64, info, timeout, sizeof(sigset64_t));
|
||||
}
|
||||
|
||||
static inline int fexecve(int fd, char* const* argv, char* const* envp) {
|
||||
syscall(__NR_execveat, fd, "", argv, envp, AT_EMPTY_PATH);
|
||||
if (errno == ENOSYS) {
|
||||
char buf[256];
|
||||
std::snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd);
|
||||
execve(buf, argv, envp);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
14
native/src/base/new.cpp
Normal file
14
native/src/base/new.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Override libc++ new implementation
|
||||
* to optimize final build size */
|
||||
|
||||
void* operator new(std::size_t s) { return malloc(s); }
|
||||
void* operator new[](std::size_t s) { return malloc(s); }
|
||||
void operator delete(void *p) { free(p); }
|
||||
void operator delete[](void *p) { free(p); }
|
||||
void* operator new(std::size_t s, const std::nothrow_t&) noexcept { return malloc(s); }
|
||||
void* operator new[](std::size_t s, const std::nothrow_t&) noexcept { return malloc(s); }
|
||||
void operator delete(void *p, const std::nothrow_t&) noexcept { free(p); }
|
||||
void operator delete[](void *p, const std::nothrow_t&) noexcept { free(p); }
|
||||
129
native/src/base/selinux.cpp
Normal file
129
native/src/base/selinux.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/xattr.h>
|
||||
|
||||
#include <base.hpp>
|
||||
#include <selinux.hpp>
|
||||
#include <flags.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Stub implementation
|
||||
|
||||
static int stub(const char *) { return 0; }
|
||||
|
||||
static int stub(const char *, const char *) { return 0; }
|
||||
|
||||
static int stub(const char *, char **ctx) {
|
||||
*ctx = strdup("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stub(int, const char *) { return 0; }
|
||||
|
||||
static int stub(int, char **ctx) {
|
||||
*ctx = strdup("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Builtin implementation
|
||||
|
||||
static void __freecon(char *s) {
|
||||
free(s);
|
||||
}
|
||||
|
||||
static int __setcon(const char *ctx) {
|
||||
int fd = open("/proc/self/attr/current", O_WRONLY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
size_t len = strlen(ctx) + 1;
|
||||
int rc = write(fd, ctx, len);
|
||||
close(fd);
|
||||
return rc != len;
|
||||
}
|
||||
|
||||
static int __getfilecon(const char *path, char **ctx) {
|
||||
char buf[1024];
|
||||
int rc = syscall(__NR_getxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1);
|
||||
if (rc >= 0)
|
||||
*ctx = strdup(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __lgetfilecon(const char *path, char **ctx) {
|
||||
char buf[1024];
|
||||
int rc = syscall(__NR_lgetxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1);
|
||||
if (rc >= 0)
|
||||
*ctx = strdup(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __fgetfilecon(int fd, char **ctx) {
|
||||
char buf[1024];
|
||||
int rc = syscall(__NR_fgetxattr, fd, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1);
|
||||
if (rc >= 0)
|
||||
*ctx = strdup(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __setfilecon(const char *path, const char *ctx) {
|
||||
return syscall(__NR_setxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
|
||||
}
|
||||
|
||||
static int __lsetfilecon(const char *path, const char *ctx) {
|
||||
return syscall(__NR_lsetxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
|
||||
}
|
||||
|
||||
static int __fsetfilecon(int fd, const char *ctx) {
|
||||
return syscall(__NR_fsetxattr, fd, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
|
||||
}
|
||||
|
||||
// Function pointers
|
||||
|
||||
void (*freecon)(char *) = __freecon;
|
||||
int (*setcon)(const char *) = stub;
|
||||
int (*getfilecon)(const char *, char **) = stub;
|
||||
int (*lgetfilecon)(const char *, char **) = stub;
|
||||
int (*fgetfilecon)(int, char **) = stub;
|
||||
int (*setfilecon)(const char *, const char *) = stub;
|
||||
int (*lsetfilecon)(const char *, const char *) = stub;
|
||||
int (*fsetfilecon)(int, const char *) = stub;
|
||||
|
||||
void getfilecon_at(int dirfd, const char *name, char **con) {
|
||||
char path[4096];
|
||||
fd_pathat(dirfd, name, path, sizeof(path));
|
||||
if (lgetfilecon(path, con))
|
||||
*con = strdup("");
|
||||
}
|
||||
|
||||
void setfilecon_at(int dirfd, const char *name, const char *con) {
|
||||
char path[4096];
|
||||
fd_pathat(dirfd, name, path, sizeof(path));
|
||||
lsetfilecon(path, con);
|
||||
}
|
||||
|
||||
#if MAGISK_DEBUG
|
||||
static bool se_state = false;
|
||||
bool selinux_enabled() {
|
||||
return se_state;
|
||||
}
|
||||
#else
|
||||
bool selinux_enabled() {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void enable_selinux() {
|
||||
#if MAGISK_DEBUG
|
||||
if (access(SELINUX_MNT, F_OK) != 0)
|
||||
return;
|
||||
se_state = true;
|
||||
#endif
|
||||
setcon = __setcon;
|
||||
getfilecon = __getfilecon;
|
||||
lgetfilecon = __lgetfilecon;
|
||||
fgetfilecon = __fgetfilecon;
|
||||
setfilecon = __setfilecon;
|
||||
lsetfilecon = __lsetfilecon;
|
||||
fsetfilecon = __fsetfilecon;
|
||||
}
|
||||
261
native/src/base/stream.cpp
Normal file
261
native/src/base/stream.cpp
Normal file
@@ -0,0 +1,261 @@
|
||||
#include <unistd.h>
|
||||
#include <cstddef>
|
||||
|
||||
#include <base.hpp>
|
||||
#include <stream.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static int strm_read(void *v, char *buf, int len) {
|
||||
auto strm = static_cast<stream *>(v);
|
||||
return strm->read(buf, len);
|
||||
}
|
||||
|
||||
static int strm_write(void *v, const char *buf, int len) {
|
||||
auto strm = static_cast<stream *>(v);
|
||||
if (!strm->write(buf, len))
|
||||
return -1;
|
||||
return len;
|
||||
}
|
||||
|
||||
static fpos_t strm_seek(void *v, fpos_t off, int whence) {
|
||||
auto strm = static_cast<stream *>(v);
|
||||
return strm->seek(off, whence);
|
||||
}
|
||||
|
||||
static int strm_close(void *v) {
|
||||
auto strm = static_cast<stream *>(v);
|
||||
delete strm;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sFILE make_stream_fp(stream_ptr &&strm) {
|
||||
auto fp = make_file(funopen(strm.release(), strm_read, strm_write, strm_seek, strm_close));
|
||||
setbuf(fp.get(), nullptr);
|
||||
return fp;
|
||||
}
|
||||
|
||||
ssize_t stream::read(void *buf, size_t len) {
|
||||
LOGE("This stream does not implement read\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t stream::readFully(void *buf, size_t len) {
|
||||
size_t read_sz = 0;
|
||||
ssize_t ret;
|
||||
do {
|
||||
ret = read((byte *) buf + read_sz, len - read_sz);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return ret;
|
||||
}
|
||||
read_sz += ret;
|
||||
} while (read_sz != len && ret != 0);
|
||||
return read_sz;
|
||||
}
|
||||
|
||||
ssize_t stream::readv(const iovec *iov, int iovcnt) {
|
||||
size_t read_sz = 0;
|
||||
for (int i = 0; i < iovcnt; ++i) {
|
||||
auto ret = readFully(iov[i].iov_base, iov[i].iov_len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
read_sz += ret;
|
||||
}
|
||||
return read_sz;
|
||||
}
|
||||
|
||||
bool stream::write(const void *buf, size_t len) {
|
||||
LOGE("This stream does not implement write\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t stream::writev(const iovec *iov, int iovcnt) {
|
||||
size_t write_sz = 0;
|
||||
for (int i = 0; i < iovcnt; ++i) {
|
||||
if (!write(iov[i].iov_base, iov[i].iov_len))
|
||||
return write_sz;
|
||||
write_sz += iov[i].iov_len;
|
||||
}
|
||||
return write_sz;
|
||||
}
|
||||
|
||||
off_t stream::seek(off_t off, int whence) {
|
||||
LOGE("This stream does not implement seek\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t fp_stream::read(void *buf, size_t len) {
|
||||
auto ret = fread(buf, 1, len, fp.get());
|
||||
return ret ? ret : (ferror(fp.get()) ? -1 : 0);
|
||||
}
|
||||
|
||||
ssize_t fp_stream::do_write(const void *buf, size_t len) {
|
||||
return fwrite(buf, 1, len, fp.get());
|
||||
}
|
||||
|
||||
off_t fp_stream::seek(off_t off, int whence) {
|
||||
return fseek(fp.get(), off, whence);
|
||||
}
|
||||
|
||||
ssize_t filter_stream::read(void *buf, size_t len) {
|
||||
return base->read(buf, len);
|
||||
}
|
||||
|
||||
bool filter_stream::write(const void *buf, size_t len) {
|
||||
return base->write(buf, len);
|
||||
}
|
||||
|
||||
bool filter_stream::write(const void *buf, size_t len, bool final) {
|
||||
return write(buf, len);
|
||||
}
|
||||
|
||||
bool chunk_out_stream::write(const void *buf, size_t len) {
|
||||
return write(buf, len, false);
|
||||
}
|
||||
|
||||
bool chunk_out_stream::write(const void *_in, size_t len, bool final) {
|
||||
auto in = static_cast<const uint8_t *>(_in);
|
||||
while (len) {
|
||||
if (buf_off + len >= chunk_sz) {
|
||||
// Enough input for a chunk
|
||||
const uint8_t *src;
|
||||
if (buf_off) {
|
||||
src = _buf;
|
||||
auto copy = chunk_sz - buf_off;
|
||||
memcpy(_buf + buf_off, in, copy);
|
||||
in += copy;
|
||||
len -= copy;
|
||||
buf_off = 0;
|
||||
} else {
|
||||
src = in;
|
||||
in += chunk_sz;
|
||||
len -= chunk_sz;
|
||||
}
|
||||
if (!write_chunk(src, chunk_sz, final && len == 0))
|
||||
return false;
|
||||
} else if (final) {
|
||||
// Final input data, write regardless whether it is chunk sized
|
||||
if (buf_off) {
|
||||
memcpy(_buf + buf_off, in, len);
|
||||
auto avail = buf_off + len;
|
||||
buf_off = 0;
|
||||
if (!write_chunk(_buf, avail, true))
|
||||
return false;
|
||||
} else {
|
||||
if (!write_chunk(in, len, true))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
// Buffer internally
|
||||
if (!_buf) {
|
||||
_buf = new uint8_t[buf_sz];
|
||||
}
|
||||
memcpy(_buf + buf_off, in, len);
|
||||
buf_off += len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void chunk_out_stream::finalize() {
|
||||
if (buf_off) {
|
||||
if (!write_chunk(_buf, buf_off, true)) {
|
||||
LOGE("Error in finalize, file truncated\n");
|
||||
}
|
||||
delete[] _buf;
|
||||
_buf = nullptr;
|
||||
buf_off = 0;
|
||||
}
|
||||
}
|
||||
|
||||
byte_stream::byte_stream(uint8_t *&buf, size_t &len) : _buf(buf), _len(len) {
|
||||
buf = nullptr;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
ssize_t byte_stream::read(void *buf, size_t len) {
|
||||
len = std::min((size_t) len, _len - _pos);
|
||||
memcpy(buf, _buf + _pos, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
bool byte_stream::write(const void *buf, size_t len) {
|
||||
resize(_pos + len);
|
||||
memcpy(_buf + _pos, buf, len);
|
||||
_pos += len;
|
||||
_len = std::max(_len, _pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
off_t byte_stream::seek(off_t off, int whence) {
|
||||
off_t np;
|
||||
switch (whence) {
|
||||
case SEEK_CUR:
|
||||
np = _pos + off;
|
||||
break;
|
||||
case SEEK_END:
|
||||
np = _len + off;
|
||||
break;
|
||||
case SEEK_SET:
|
||||
np = off;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
resize(np, true);
|
||||
_pos = np;
|
||||
return np;
|
||||
}
|
||||
|
||||
void byte_stream::resize(size_t new_pos, bool zero) {
|
||||
bool resize = false;
|
||||
size_t old_cap = _cap;
|
||||
while (new_pos > _cap) {
|
||||
_cap = _cap ? (_cap << 1) - (_cap >> 1) : 1 << 12;
|
||||
resize = true;
|
||||
}
|
||||
if (resize) {
|
||||
_buf = (uint8_t *) xrealloc(_buf, _cap);
|
||||
if (zero)
|
||||
memset(_buf + old_cap, 0, _cap - old_cap);
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t fd_stream::read(void *buf, size_t len) {
|
||||
return ::read(fd, buf, len);
|
||||
}
|
||||
|
||||
ssize_t fd_stream::readv(const iovec *iov, int iovcnt) {
|
||||
return ::readv(fd, iov, iovcnt);
|
||||
}
|
||||
|
||||
ssize_t fd_stream::do_write(const void *buf, size_t len) {
|
||||
return ::write(fd, buf, len);
|
||||
}
|
||||
|
||||
ssize_t fd_stream::writev(const iovec *iov, int iovcnt) {
|
||||
return ::writev(fd, iov, iovcnt);
|
||||
}
|
||||
|
||||
off_t fd_stream::seek(off_t off, int whence) {
|
||||
return lseek(fd, off, whence);
|
||||
}
|
||||
|
||||
bool file_stream::write(const void *buf, size_t len) {
|
||||
size_t write_sz = 0;
|
||||
ssize_t ret;
|
||||
do {
|
||||
ret = do_write((byte *) buf + write_sz, len - write_sz);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
write_sz += ret;
|
||||
} while (write_sz != len && ret != 0);
|
||||
return true;
|
||||
}
|
||||
524
native/src/base/xwrap.cpp
Normal file
524
native/src/base/xwrap.cpp
Normal file
@@ -0,0 +1,524 @@
|
||||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/inotify.h>
|
||||
|
||||
#include <base.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
FILE *xfopen(const char *pathname, const char *mode) {
|
||||
FILE *fp = fopen(pathname, mode);
|
||||
if (fp == nullptr) {
|
||||
PLOGE("fopen: %s", pathname);
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
FILE *xfdopen(int fd, const char *mode) {
|
||||
FILE *fp = fdopen(fd, mode);
|
||||
if (fp == nullptr) {
|
||||
PLOGE("fopen");
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
int xopen(const char *pathname, int flags) {
|
||||
int fd = open(pathname, flags);
|
||||
if (fd < 0) {
|
||||
PLOGE("open: %s", pathname);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int xopen(const char *pathname, int flags, mode_t mode) {
|
||||
int fd = open(pathname, flags, mode);
|
||||
if (fd < 0) {
|
||||
PLOGE("open: %s", pathname);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int xopenat(int dirfd, const char *pathname, int flags) {
|
||||
int fd = openat(dirfd, pathname, flags);
|
||||
if (fd < 0) {
|
||||
PLOGE("openat: %s", pathname);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int xopenat(int dirfd, const char *pathname, int flags, mode_t mode) {
|
||||
int fd = openat(dirfd, pathname, flags, mode);
|
||||
if (fd < 0) {
|
||||
PLOGE("openat: %s", pathname);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
// Write exact same size as count
|
||||
ssize_t xwrite(int fd, const void *buf, size_t count) {
|
||||
size_t write_sz = 0;
|
||||
ssize_t ret;
|
||||
do {
|
||||
ret = write(fd, (byte *) buf + write_sz, count - write_sz);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
PLOGE("write");
|
||||
return ret;
|
||||
}
|
||||
write_sz += ret;
|
||||
} while (write_sz != count && ret != 0);
|
||||
if (write_sz != count) {
|
||||
PLOGE("write (%zu != %zu)", count, write_sz);
|
||||
}
|
||||
return write_sz;
|
||||
}
|
||||
|
||||
// Read error other than EOF
|
||||
ssize_t xread(int fd, void *buf, size_t count) {
|
||||
int ret = read(fd, buf, count);
|
||||
if (ret < 0) {
|
||||
PLOGE("read");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Read exact same size as count
|
||||
ssize_t xxread(int fd, void *buf, size_t count) {
|
||||
size_t read_sz = 0;
|
||||
ssize_t ret;
|
||||
do {
|
||||
ret = read(fd, (byte *) buf + read_sz, count - read_sz);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
PLOGE("read");
|
||||
return ret;
|
||||
}
|
||||
read_sz += ret;
|
||||
} while (read_sz != count && ret != 0);
|
||||
if (read_sz != count) {
|
||||
PLOGE("read (%zu != %zu)", count, read_sz);
|
||||
}
|
||||
return read_sz;
|
||||
}
|
||||
|
||||
off_t xlseek(int fd, off_t offset, int whence) {
|
||||
off_t ret = lseek(fd, offset, whence);
|
||||
if (ret < 0) {
|
||||
PLOGE("lseek");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xpipe2(int pipefd[2], int flags) {
|
||||
int ret = pipe2(pipefd, flags);
|
||||
if (ret < 0) {
|
||||
PLOGE("pipe2");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xsetns(int fd, int nstype) {
|
||||
int ret = setns(fd, nstype);
|
||||
if (ret < 0) {
|
||||
PLOGE("setns");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xunshare(int flags) {
|
||||
int ret = unshare(flags);
|
||||
if (ret < 0) {
|
||||
PLOGE("unshare");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
DIR *xopendir(const char *name) {
|
||||
DIR *d = opendir(name);
|
||||
if (d == nullptr) {
|
||||
PLOGE("opendir: %s", name);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
DIR *xfdopendir(int fd) {
|
||||
DIR *d = fdopendir(fd);
|
||||
if (d == nullptr) {
|
||||
PLOGE("fdopendir");
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
struct dirent *xreaddir(DIR *dirp) {
|
||||
errno = 0;
|
||||
for (dirent *e;;) {
|
||||
e = readdir(dirp);
|
||||
if (e == nullptr) {
|
||||
if (errno)
|
||||
PLOGE("readdir");
|
||||
return nullptr;
|
||||
} else if (e->d_name == "."sv || e->d_name == ".."sv) {
|
||||
// Filter . and .. for users
|
||||
continue;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
pid_t xsetsid() {
|
||||
pid_t pid = setsid();
|
||||
if (pid < 0) {
|
||||
PLOGE("setsid");
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
int xsocket(int domain, int type, int protocol) {
|
||||
int fd = socket(domain, type, protocol);
|
||||
if (fd < 0) {
|
||||
PLOGE("socket");
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
|
||||
int ret = bind(sockfd, addr, addrlen);
|
||||
if (ret < 0) {
|
||||
PLOGE("bind");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xlisten(int sockfd, int backlog) {
|
||||
int ret = listen(sockfd, backlog);
|
||||
if (ret < 0) {
|
||||
PLOGE("listen");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xaccept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) {
|
||||
int fd = accept4(sockfd, addr, addrlen, flags);
|
||||
if (fd < 0) {
|
||||
PLOGE("accept4");
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
void *xmalloc(size_t size) {
|
||||
void *p = malloc(size);
|
||||
if (p == nullptr) {
|
||||
PLOGE("malloc");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void *xcalloc(size_t nmemb, size_t size) {
|
||||
void *p = calloc(nmemb, size);
|
||||
if (p == nullptr) {
|
||||
PLOGE("calloc");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void *xrealloc(void *ptr, size_t size) {
|
||||
void *p = realloc(ptr, size);
|
||||
if (p == nullptr) {
|
||||
PLOGE("realloc");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags) {
|
||||
int sent = sendmsg(sockfd, msg, flags);
|
||||
if (sent < 0) {
|
||||
PLOGE("sendmsg");
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) {
|
||||
int rec = recvmsg(sockfd, msg, flags);
|
||||
if (rec < 0) {
|
||||
PLOGE("recvmsg");
|
||||
}
|
||||
return rec;
|
||||
}
|
||||
|
||||
int xpthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||
void *(*start_routine) (void *), void *arg) {
|
||||
errno = pthread_create(thread, attr, start_routine, arg);
|
||||
if (errno) {
|
||||
PLOGE("pthread_create");
|
||||
}
|
||||
return errno;
|
||||
}
|
||||
|
||||
int xaccess(const char *path, int mode) {
|
||||
int ret = access(path, mode);
|
||||
if (ret < 0) {
|
||||
PLOGE("access %s", path);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xstat(const char *pathname, struct stat *buf) {
|
||||
int ret = stat(pathname, buf);
|
||||
if (ret < 0) {
|
||||
PLOGE("stat %s", pathname);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xlstat(const char *pathname, struct stat *buf) {
|
||||
int ret = lstat(pathname, buf);
|
||||
if (ret < 0) {
|
||||
PLOGE("lstat %s", pathname);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xfstat(int fd, struct stat *buf) {
|
||||
int ret = fstat(fd, buf);
|
||||
if (ret < 0) {
|
||||
PLOGE("fstat %d", fd);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xfstatat(int dirfd, const char *pathname, struct stat *buf, int flags) {
|
||||
int ret = fstatat(dirfd, pathname, buf, flags);
|
||||
if (ret < 0) {
|
||||
PLOGE("fstatat %s", pathname);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xdup(int fd) {
|
||||
int ret = dup(fd);
|
||||
if (ret < 0) {
|
||||
PLOGE("dup");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xdup2(int oldfd, int newfd) {
|
||||
int ret = dup2(oldfd, newfd);
|
||||
if (ret < 0) {
|
||||
PLOGE("dup2");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xdup3(int oldfd, int newfd, int flags) {
|
||||
int ret = dup3(oldfd, newfd, flags);
|
||||
if (ret < 0) {
|
||||
PLOGE("dup3");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz) {
|
||||
ssize_t ret = readlink(pathname, buf, bufsiz);
|
||||
if (ret < 0) {
|
||||
PLOGE("readlink %s", pathname);
|
||||
} else {
|
||||
buf[ret] = '\0';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t xreadlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) {
|
||||
// readlinkat() may fail on x86 platform, returning random value
|
||||
// instead of number of bytes placed in buf (length of link)
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
memset(buf, 0, bufsiz);
|
||||
ssize_t ret = readlinkat(dirfd, pathname, buf, bufsiz);
|
||||
if (ret < 0) {
|
||||
PLOGE("readlinkat %s", pathname);
|
||||
}
|
||||
return ret;
|
||||
#else
|
||||
ssize_t ret = readlinkat(dirfd, pathname, buf, bufsiz);
|
||||
if (ret < 0) {
|
||||
PLOGE("readlinkat %s", pathname);
|
||||
} else {
|
||||
buf[ret] = '\0';
|
||||
}
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
int xfaccessat(int dirfd, const char *pathname) {
|
||||
int ret = faccessat(dirfd, pathname, F_OK, 0);
|
||||
if (ret < 0) {
|
||||
PLOGE("faccessat %s", pathname);
|
||||
}
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
if (ret > 0 && errno == 0) {
|
||||
LOGD("faccessat success but ret is %d\n", ret);
|
||||
ret = 0;
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xsymlink(const char *target, const char *linkpath) {
|
||||
int ret = symlink(target, linkpath);
|
||||
if (ret < 0) {
|
||||
PLOGE("symlink %s->%s", target, linkpath);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xsymlinkat(const char *target, int newdirfd, const char *linkpath) {
|
||||
int ret = symlinkat(target, newdirfd, linkpath);
|
||||
if (ret < 0) {
|
||||
PLOGE("symlinkat %s->%s", target, linkpath);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xlinkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags) {
|
||||
int ret = linkat(olddirfd, oldpath, newdirfd, newpath, flags);
|
||||
if (ret < 0) {
|
||||
PLOGE("linkat %s->%s", oldpath, newpath);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xmount(const char *source, const char *target,
|
||||
const char *filesystemtype, unsigned long mountflags,
|
||||
const void *data) {
|
||||
int ret = mount(source, target, filesystemtype, mountflags, data);
|
||||
if (ret < 0) {
|
||||
PLOGE("mount %s->%s", source, target);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xumount(const char *target) {
|
||||
int ret = umount(target);
|
||||
if (ret < 0) {
|
||||
PLOGE("umount %s", target);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xumount2(const char *target, int flags) {
|
||||
int ret = umount2(target, flags);
|
||||
if (ret < 0) {
|
||||
PLOGE("umount2 %s", target);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xrename(const char *oldpath, const char *newpath) {
|
||||
int ret = rename(oldpath, newpath);
|
||||
if (ret < 0) {
|
||||
PLOGE("rename %s->%s", oldpath, newpath);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xmkdir(const char *pathname, mode_t mode) {
|
||||
int ret = mkdir(pathname, mode);
|
||||
if (ret < 0 && errno != EEXIST) {
|
||||
PLOGE("mkdir %s %u", pathname, mode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xmkdirs(const char *pathname, mode_t mode) {
|
||||
int ret = mkdirs(pathname, mode);
|
||||
if (ret < 0) {
|
||||
PLOGE("mkdirs %s", pathname);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xmkdirat(int dirfd, const char *pathname, mode_t mode) {
|
||||
int ret = mkdirat(dirfd, pathname, mode);
|
||||
if (ret < 0 && errno != EEXIST) {
|
||||
PLOGE("mkdirat %s %u", pathname, mode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *xmmap(void *addr, size_t length, int prot, int flags,
|
||||
int fd, off_t offset) {
|
||||
void *ret = mmap(addr, length, prot, flags, fd, offset);
|
||||
if (ret == MAP_FAILED) {
|
||||
PLOGE("mmap");
|
||||
return nullptr;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count) {
|
||||
ssize_t ret = sendfile(out_fd, in_fd, offset, count);
|
||||
if (ret < 0) {
|
||||
PLOGE("sendfile");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
pid_t xfork() {
|
||||
int ret = fork();
|
||||
if (ret < 0) {
|
||||
PLOGE("fork");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xpoll(struct pollfd *fds, nfds_t nfds, int timeout) {
|
||||
int ret = poll(fds, nfds, timeout);
|
||||
if (ret < 0) {
|
||||
PLOGE("poll");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xinotify_init1(int flags) {
|
||||
int ret = inotify_init1(flags);
|
||||
if (ret < 0) {
|
||||
PLOGE("inotify_init1");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *xrealpath(const char *path, char *resolved_path) {
|
||||
char buf[PATH_MAX];
|
||||
char *ret = realpath(path, buf);
|
||||
if (ret == nullptr) {
|
||||
PLOGE("xrealpath");
|
||||
} else {
|
||||
strcpy(resolved_path, buf);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xmknod(const char *pathname, mode_t mode, dev_t dev) {
|
||||
int ret = mknod(pathname, mode, dev);
|
||||
if (ret < 0) {
|
||||
PLOGE("mknod");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
long xptrace(int request, pid_t pid, void *addr, void *data) {
|
||||
long ret = ptrace(request, pid, addr, data);
|
||||
if (ret < 0)
|
||||
PLOGE("ptrace %d", pid);
|
||||
return ret;
|
||||
}
|
||||
71
native/src/base/xwrap.hpp
Normal file
71
native/src/base/xwrap.hpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <poll.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
FILE *xfopen(const char *pathname, const char *mode);
|
||||
FILE *xfdopen(int fd, const char *mode);
|
||||
int xopen(const char *pathname, int flags);
|
||||
int xopen(const char *pathname, int flags, mode_t mode);
|
||||
int xopenat(int dirfd, const char *pathname, int flags);
|
||||
int xopenat(int dirfd, const char *pathname, int flags, mode_t mode);
|
||||
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);
|
||||
off_t xlseek(int fd, off_t offset, int whence);
|
||||
int xpipe2(int pipefd[2], int flags);
|
||||
int xsetns(int fd, int nstype);
|
||||
int xunshare(int flags);
|
||||
DIR *xopendir(const char *name);
|
||||
DIR *xfdopendir(int fd);
|
||||
struct dirent *xreaddir(DIR *dirp);
|
||||
pid_t xsetsid();
|
||||
int xsocket(int domain, int type, int protocol);
|
||||
int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
||||
int xconnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
||||
int xlisten(int sockfd, int backlog);
|
||||
int xaccept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
|
||||
void *xmalloc(size_t size);
|
||||
void *xcalloc(size_t nmemb, size_t size);
|
||||
void *xrealloc(void *ptr, size_t size);
|
||||
ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags);
|
||||
ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags);
|
||||
int xpthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||
void *(*start_routine) (void *), void *arg);
|
||||
int xaccess(const char *path, int mode);
|
||||
int xstat(const char *pathname, struct stat *buf);
|
||||
int xlstat(const char *pathname, struct stat *buf);
|
||||
int xfstat(int fd, struct stat *buf);
|
||||
int xfstatat(int dirfd, const char *pathname, struct stat *buf, int flags);
|
||||
int xdup(int fd);
|
||||
int xdup2(int oldfd, int newfd);
|
||||
int xdup3(int oldfd, int newfd, int flags);
|
||||
ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz);
|
||||
ssize_t xreadlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz);
|
||||
int xfaccessat(int dirfd, const char *pathname);
|
||||
int xsymlink(const char *target, const char *linkpath);
|
||||
int xsymlinkat(const char *target, int newdirfd, const char *linkpath);
|
||||
int xlinkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags);
|
||||
int xmount(const char *source, const char *target,
|
||||
const char *filesystemtype, unsigned long mountflags,
|
||||
const void *data);
|
||||
int xumount(const char *target);
|
||||
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);
|
||||
int xmkdirat(int dirfd, const char *pathname, mode_t mode);
|
||||
void *xmmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
|
||||
ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count);
|
||||
pid_t xfork();
|
||||
int xpoll(struct pollfd *fds, nfds_t nfds, int timeout);
|
||||
int xinotify_init1(int flags);
|
||||
char *xrealpath(const char *path, char *resolved_path);
|
||||
int xmknod(const char *pathname, mode_t mode, dev_t dev);
|
||||
long xptrace(int request, pid_t pid, void *addr = nullptr, void *data = nullptr);
|
||||
static inline long xptrace(int request, pid_t pid, void *addr, uintptr_t data) {
|
||||
return xptrace(request, pid, addr, reinterpret_cast<void *>(data));
|
||||
}
|
||||
#define WEVENT(s) (((s) & 0xffff0000) >> 16)
|
||||
Reference in New Issue
Block a user