Convert indentation to spaces

The tab war is lost
This commit is contained in:
topjohnwu
2020-12-30 22:11:24 -08:00
parent 947a7d6a2f
commit f9bde347bc
78 changed files with 10945 additions and 10945 deletions

View File

@@ -12,25 +12,25 @@ LOCAL_STATIC_LIBRARIES := libnanopb libsystemproperties libutils
LOCAL_C_INCLUDES := jni/include LOCAL_C_INCLUDES := jni/include
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
core/applets.cpp \ core/applets.cpp \
core/magisk.cpp \ core/magisk.cpp \
core/daemon.cpp \ core/daemon.cpp \
core/bootstages.cpp \ core/bootstages.cpp \
core/socket.cpp \ core/socket.cpp \
core/db.cpp \ core/db.cpp \
core/scripting.cpp \ core/scripting.cpp \
core/restorecon.cpp \ core/restorecon.cpp \
core/module.cpp \ core/module.cpp \
magiskhide/magiskhide.cpp \ magiskhide/magiskhide.cpp \
magiskhide/proc_monitor.cpp \ magiskhide/proc_monitor.cpp \
magiskhide/hide_utils.cpp \ magiskhide/hide_utils.cpp \
magiskhide/hide_policy.cpp \ magiskhide/hide_policy.cpp \
resetprop/persist_properties.cpp \ resetprop/persist_properties.cpp \
resetprop/resetprop.cpp \ resetprop/resetprop.cpp \
su/su.cpp \ su/su.cpp \
su/connect.cpp \ su/connect.cpp \
su/pts.cpp \ su/pts.cpp \
su/su_daemon.cpp su/su_daemon.cpp
LOCAL_LDLIBS := -llog LOCAL_LDLIBS := -llog
include $(BUILD_EXECUTABLE) include $(BUILD_EXECUTABLE)
@@ -52,24 +52,24 @@ ifdef BB_INIT
LOCAL_STATIC_LIBRARIES := libsepol libxz libutils LOCAL_STATIC_LIBRARIES := libsepol libxz libutils
LOCAL_C_INCLUDES := \ LOCAL_C_INCLUDES := \
jni/include \ jni/include \
out \ out \
out/$(TARGET_ARCH_ABI) out/$(TARGET_ARCH_ABI)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
init/init.cpp \ init/init.cpp \
init/mount.cpp \ init/mount.cpp \
init/rootdir.cpp \ init/rootdir.cpp \
init/getinfo.cpp \ init/getinfo.cpp \
init/twostage.cpp \ init/twostage.cpp \
init/raw_data.cpp \ init/raw_data.cpp \
core/socket.cpp \ core/socket.cpp \
magiskpolicy/sepolicy.cpp \ magiskpolicy/sepolicy.cpp \
magiskpolicy/magiskpolicy.cpp \ magiskpolicy/magiskpolicy.cpp \
magiskpolicy/rules.cpp \ magiskpolicy/rules.cpp \
magiskpolicy/policydb.cpp \ magiskpolicy/policydb.cpp \
magiskpolicy/statement.cpp \ magiskpolicy/statement.cpp \
magiskboot/pattern.cpp magiskboot/pattern.cpp
LOCAL_LDFLAGS := -static LOCAL_LDFLAGS := -static
include $(BUILD_EXECUTABLE) include $(BUILD_EXECUTABLE)
@@ -84,15 +84,15 @@ LOCAL_STATIC_LIBRARIES := libmincrypt liblzma liblz4 libbz2 libfdt libutils
LOCAL_C_INCLUDES := jni/include LOCAL_C_INCLUDES := jni/include
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
magiskboot/main.cpp \ magiskboot/main.cpp \
magiskboot/bootimg.cpp \ magiskboot/bootimg.cpp \
magiskboot/hexpatch.cpp \ magiskboot/hexpatch.cpp \
magiskboot/compress.cpp \ magiskboot/compress.cpp \
magiskboot/format.cpp \ magiskboot/format.cpp \
magiskboot/dtb.cpp \ magiskboot/dtb.cpp \
magiskboot/ramdisk.cpp \ magiskboot/ramdisk.cpp \
magiskboot/pattern.cpp \ magiskboot/pattern.cpp \
utils/cpio.cpp utils/cpio.cpp
LOCAL_LDLIBS := -lz LOCAL_LDLIBS := -lz
LOCAL_LDFLAGS := -static LOCAL_LDFLAGS := -static
@@ -108,12 +108,12 @@ LOCAL_STATIC_LIBRARIES := libsepol libutils
LOCAL_C_INCLUDES := jni/include LOCAL_C_INCLUDES := jni/include
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
core/applet_stub.cpp \ core/applet_stub.cpp \
magiskpolicy/sepolicy.cpp \ magiskpolicy/sepolicy.cpp \
magiskpolicy/magiskpolicy.cpp \ magiskpolicy/magiskpolicy.cpp \
magiskpolicy/rules.cpp \ magiskpolicy/rules.cpp \
magiskpolicy/policydb.cpp \ magiskpolicy/policydb.cpp \
magiskpolicy/statement.cpp magiskpolicy/statement.cpp
LOCAL_CFLAGS := -DAPPLET_STUB_MAIN=magiskpolicy_main LOCAL_CFLAGS := -DAPPLET_STUB_MAIN=magiskpolicy_main
LOCAL_LDFLAGS := -static LOCAL_LDFLAGS := -static
@@ -129,9 +129,9 @@ LOCAL_STATIC_LIBRARIES := libnanopb libsystemproperties libutils
LOCAL_C_INCLUDES := jni/include LOCAL_C_INCLUDES := jni/include
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
core/applet_stub.cpp \ core/applet_stub.cpp \
resetprop/persist_properties.cpp \ resetprop/persist_properties.cpp \
resetprop/resetprop.cpp \ resetprop/resetprop.cpp \
LOCAL_CFLAGS := -DAPPLET_STUB_MAIN=resetprop_main LOCAL_CFLAGS := -DAPPLET_STUB_MAIN=resetprop_main
LOCAL_LDFLAGS := -static LOCAL_LDFLAGS := -static

View File

@@ -4,7 +4,7 @@
#include <utils.hpp> #include <utils.hpp>
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
umask(0); umask(0);
cmdline_logging(); cmdline_logging();
return APPLET_STUB_MAIN(argc, argv); return APPLET_STUB_MAIN(argc, argv);
} }

View File

@@ -15,32 +15,32 @@ using main_fun = int (*)(int, char *[]);
static main_fun applet_main[] = { su_client_main, resetprop_main, magiskhide_main, nullptr }; static main_fun applet_main[] = { su_client_main, resetprop_main, magiskhide_main, nullptr };
[[noreturn]] static void call_applet(int argc, char **argv) { [[noreturn]] static void call_applet(int argc, char **argv) {
// Applets // Applets
for (int i = 0; applet_names[i]; ++i) { for (int i = 0; applet_names[i]; ++i) {
if (strcmp(basename(argv[0]), applet_names[i]) == 0) { if (strcmp(basename(argv[0]), applet_names[i]) == 0) {
exit((*applet_main[i])(argc, argv)); exit((*applet_main[i])(argc, argv));
} }
} }
fprintf(stderr, "%s: applet not found\n", basename(argv[0])); fprintf(stderr, "%s: applet not found\n", basename(argv[0]));
exit(1); exit(1);
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
umask(0); umask(0);
dload_selinux(); dload_selinux();
cmdline_logging(); cmdline_logging();
init_argv0(argc, argv); init_argv0(argc, argv);
if (basename(argv[0]) == "magisk"sv) { if (basename(argv[0]) == "magisk"sv) {
if (argc > 1 && argv[1][0] != '-') { if (argc > 1 && argv[1][0] != '-') {
// Calling applet via magisk [applet] args // Calling applet via magisk [applet] args
--argc; --argc;
++argv; ++argv;
} else { } else {
return magisk_main(argc, argv); return magisk_main(argc, argv);
} }
} }
call_applet(argc, argv); call_applet(argc, argv);
} }

View File

@@ -37,64 +37,64 @@ static bool safe_mode = false;
#define mount_mirror(part, flag) \ #define mount_mirror(part, flag) \
else if (MNT_DIR_IS("/" #part) && me->mnt_type != "tmpfs"sv && lstat(me->mnt_dir, &st) == 0) \ else if (MNT_DIR_IS("/" #part) && me->mnt_type != "tmpfs"sv && lstat(me->mnt_dir, &st) == 0) \
do_mount_mirror(part, flag) do_mount_mirror(part, flag)
#define link_mirror(part) \ #define link_mirror(part) \
SETMIR(buf1, part); \ SETMIR(buf1, part); \
if (access("/system/" #part, F_OK) == 0 && access(buf1, F_OK) != 0) { \ if (access("/system/" #part, F_OK) == 0 && access(buf1, F_OK) != 0) { \
xsymlink("./system/" #part, buf1); \ xsymlink("./system/" #part, buf1); \
LOGI("link: %s\n", buf1); \ LOGI("link: %s\n", buf1); \
} }
#define link_orig_dir(dir, part) \ #define link_orig_dir(dir, part) \
else if (MNT_DIR_IS(dir) && me->mnt_type != "tmpfs"sv) { \ else if (MNT_DIR_IS(dir) && me->mnt_type != "tmpfs"sv) { \
SETMIR(buf1, part); \ SETMIR(buf1, part); \
rmdir(buf1); \ rmdir(buf1); \
xsymlink(dir, buf1); \ xsymlink(dir, buf1); \
LOGI("link: %s\n", buf1); \ LOGI("link: %s\n", buf1); \
} }
#define link_orig(part) link_orig_dir("/" #part, part) #define link_orig(part) link_orig_dir("/" #part, part)
static void mount_mirrors() { static void mount_mirrors() {
char buf1[4096]; char buf1[4096];
char buf2[4096]; char buf2[4096];
LOGI("* Mounting mirrors\n"); LOGI("* Mounting mirrors\n");
parse_mnt("/proc/mounts", [&](mntent *me) { parse_mnt("/proc/mounts", [&](mntent *me) {
struct stat st; struct stat st;
if (0) {} if (0) {}
mount_mirror(system, MS_RDONLY) mount_mirror(system, MS_RDONLY)
mount_mirror(vendor, MS_RDONLY) mount_mirror(vendor, MS_RDONLY)
mount_mirror(product, MS_RDONLY) mount_mirror(product, MS_RDONLY)
mount_mirror(system_ext, MS_RDONLY) mount_mirror(system_ext, MS_RDONLY)
mount_mirror(data, 0) mount_mirror(data, 0)
link_orig(cache) link_orig(cache)
link_orig(metadata) link_orig(metadata)
link_orig(persist) link_orig(persist)
link_orig_dir("/mnt/vendor/persist", persist) link_orig_dir("/mnt/vendor/persist", persist)
else if (SDK_INT >= 24 && MNT_DIR_IS("/proc") && !strstr(me->mnt_opts, "hidepid=2")) { else if (SDK_INT >= 24 && MNT_DIR_IS("/proc") && !strstr(me->mnt_opts, "hidepid=2")) {
xmount(nullptr, "/proc", nullptr, MS_REMOUNT, "hidepid=2,gid=3009"); xmount(nullptr, "/proc", nullptr, MS_REMOUNT, "hidepid=2,gid=3009");
} }
return true; return true;
}); });
SETMIR(buf1, system); SETMIR(buf1, system);
if (access(buf1, F_OK) != 0) { if (access(buf1, F_OK) != 0) {
xsymlink("./system_root/system", buf1); xsymlink("./system_root/system", buf1);
LOGI("link: %s\n", buf1); LOGI("link: %s\n", buf1);
parse_mnt("/proc/mounts", [&](mntent *me) { parse_mnt("/proc/mounts", [&](mntent *me) {
struct stat st; struct stat st;
if (MNT_DIR_IS("/") && me->mnt_type != "rootfs"sv && stat("/", &st) == 0) { if (MNT_DIR_IS("/") && me->mnt_type != "rootfs"sv && stat("/", &st) == 0) {
do_mount_mirror(system_root, MS_RDONLY) do_mount_mirror(system_root, MS_RDONLY)
return false; return false;
} }
return true; return true;
}); });
} }
link_mirror(vendor) link_mirror(vendor)
link_mirror(product) link_mirror(product)
link_mirror(system_ext) link_mirror(system_ext)
} }
constexpr char bb_script[] = R"EOF( constexpr char bb_script[] = R"EOF(
@@ -105,167 +105,167 @@ exec /data/adb/magisk/busybox.bin "$@"
)EOF"; )EOF";
static bool magisk_env() { static bool magisk_env() {
char buf[4096]; char buf[4096];
LOGI("* Initializing Magisk environment\n"); LOGI("* Initializing Magisk environment\n");
string pkg; string pkg;
check_manager(&pkg); check_manager(&pkg);
sprintf(buf, "%s/0/%s/install", APP_DATA_DIR, pkg.data()); sprintf(buf, "%s/0/%s/install", APP_DATA_DIR, pkg.data());
// Alternative binaries paths // Alternative binaries paths
const char *alt_bin[] = { "/cache/data_adb/magisk", "/data/magisk", buf }; const char *alt_bin[] = { "/cache/data_adb/magisk", "/data/magisk", buf };
for (auto alt : alt_bin) { for (auto alt : alt_bin) {
struct stat st; struct stat st;
if (lstat(alt, &st) == 0) { if (lstat(alt, &st) == 0) {
if (S_ISLNK(st.st_mode)) { if (S_ISLNK(st.st_mode)) {
unlink(alt); unlink(alt);
continue; continue;
} }
rm_rf(DATABIN); rm_rf(DATABIN);
cp_afc(alt, DATABIN); cp_afc(alt, DATABIN);
rm_rf(alt); rm_rf(alt);
break; break;
} }
} }
// Remove stuffs // Remove stuffs
rm_rf("/cache/data_adb"); rm_rf("/cache/data_adb");
rm_rf("/data/adb/modules/.core"); rm_rf("/data/adb/modules/.core");
unlink("/data/adb/magisk.img"); unlink("/data/adb/magisk.img");
unlink("/data/adb/magisk_merge.img"); unlink("/data/adb/magisk_merge.img");
unlink("/data/magisk.img"); unlink("/data/magisk.img");
unlink("/data/magisk_merge.img"); unlink("/data/magisk_merge.img");
unlink("/data/magisk_debug.log"); unlink("/data/magisk_debug.log");
// Directories in /data/adb // Directories in /data/adb
xmkdir(DATABIN, 0755); xmkdir(DATABIN, 0755);
xmkdir(MODULEROOT, 0755); xmkdir(MODULEROOT, 0755);
xmkdir(SECURE_DIR "/post-fs-data.d", 0755); xmkdir(SECURE_DIR "/post-fs-data.d", 0755);
xmkdir(SECURE_DIR "/service.d", 0755); xmkdir(SECURE_DIR "/service.d", 0755);
// Disable/remove magiskhide, resetprop // Disable/remove magiskhide, resetprop
if (SDK_INT < 19) { if (SDK_INT < 19) {
unlink("/sbin/resetprop"); unlink("/sbin/resetprop");
unlink("/sbin/magiskhide"); unlink("/sbin/magiskhide");
} }
if (access(DATABIN "/busybox.bin", X_OK)) { if (access(DATABIN "/busybox.bin", X_OK)) {
if (access(DATABIN "/busybox", X_OK)) if (access(DATABIN "/busybox", X_OK))
return false; return false;
rename(DATABIN "/busybox", DATABIN "/busybox.bin"); rename(DATABIN "/busybox", DATABIN "/busybox.bin");
} }
sprintf(buf, "%s/" BBPATH "/busybox", MAGISKTMP.data()); sprintf(buf, "%s/" BBPATH "/busybox", MAGISKTMP.data());
{ {
auto fp = open_file(DATABIN "/busybox", "we"); auto fp = open_file(DATABIN "/busybox", "we");
fprintf(fp.get(), bb_script, buf); fprintf(fp.get(), bb_script, buf);
} }
chmod(DATABIN "/busybox", 0755); chmod(DATABIN "/busybox", 0755);
mkdir(dirname(buf), 0755); mkdir(dirname(buf), 0755);
cp_afc(DATABIN "/busybox.bin", buf); cp_afc(DATABIN "/busybox.bin", buf);
exec_command_async(buf, "--install", "-s", dirname(buf)); exec_command_async(buf, "--install", "-s", dirname(buf));
return true; return true;
} }
void reboot() { void reboot() {
if (RECOVERY_MODE) if (RECOVERY_MODE)
exec_command_sync("/system/bin/reboot", "recovery"); exec_command_sync("/system/bin/reboot", "recovery");
else else
exec_command_sync("/system/bin/reboot"); exec_command_sync("/system/bin/reboot");
} }
static bool check_data() { static bool check_data() {
bool mnt = false; bool mnt = false;
file_readline("/proc/mounts", [&](string_view s) { file_readline("/proc/mounts", [&](string_view s) {
if (str_contains(s, " /data ") && !str_contains(s, "tmpfs")) { if (str_contains(s, " /data ") && !str_contains(s, "tmpfs")) {
mnt = true; mnt = true;
return false; return false;
} }
return true; return true;
}); });
if (!mnt) if (!mnt)
return false; return false;
auto crypto = getprop("ro.crypto.state"); auto crypto = getprop("ro.crypto.state");
if (!crypto.empty()) { if (!crypto.empty()) {
if (crypto == "unencrypted") { if (crypto == "unencrypted") {
// Unencrypted, we can directly access data // Unencrypted, we can directly access data
return true; return true;
} else { } else {
// Encrypted, check whether vold is started // Encrypted, check whether vold is started
return !getprop("init.svc.vold").empty(); return !getprop("init.svc.vold").empty();
} }
} }
// ro.crypto.state is not set, assume it's unencrypted // ro.crypto.state is not set, assume it's unencrypted
return true; return true;
} }
void unlock_blocks() { void unlock_blocks() {
int fd, dev, OFF = 0; int fd, dev, OFF = 0;
auto dir = xopen_dir("/dev/block"); auto dir = xopen_dir("/dev/block");
if (!dir) if (!dir)
return; return;
dev = dirfd(dir.get()); dev = dirfd(dir.get());
for (dirent *entry; (entry = readdir(dir.get()));) { for (dirent *entry; (entry = readdir(dir.get()));) {
if (entry->d_type == DT_BLK) { if (entry->d_type == DT_BLK) {
if ((fd = openat(dev, entry->d_name, O_RDONLY | O_CLOEXEC)) < 0) if ((fd = openat(dev, entry->d_name, O_RDONLY | O_CLOEXEC)) < 0)
continue; continue;
if (ioctl(fd, BLKROSET, &OFF) < 0) if (ioctl(fd, BLKROSET, &OFF) < 0)
PLOGE("unlock %s", entry->d_name); PLOGE("unlock %s", entry->d_name);
close(fd); close(fd);
} }
} }
} }
#define test_bit(bit, array) (array[bit / 8] & (1 << (bit % 8))) #define test_bit(bit, array) (array[bit / 8] & (1 << (bit % 8)))
static bool check_key_combo() { static bool check_key_combo() {
uint8_t bitmask[(KEY_MAX + 1) / 8]; uint8_t bitmask[(KEY_MAX + 1) / 8];
vector<int> events; vector<int> events;
constexpr char name[] = "/dev/.ev"; constexpr char name[] = "/dev/.ev";
// First collect candidate events that accepts volume down // First collect candidate events that accepts volume down
for (int minor = 64; minor < 96; ++minor) { for (int minor = 64; minor < 96; ++minor) {
if (xmknod(name, S_IFCHR | 0444, makedev(13, minor))) if (xmknod(name, S_IFCHR | 0444, makedev(13, minor)))
continue; continue;
int fd = open(name, O_RDONLY | O_CLOEXEC); int fd = open(name, O_RDONLY | O_CLOEXEC);
unlink(name); unlink(name);
if (fd < 0) if (fd < 0)
continue; continue;
memset(bitmask, 0, sizeof(bitmask)); memset(bitmask, 0, sizeof(bitmask));
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask); ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask);
if (test_bit(KEY_VOLUMEDOWN, bitmask)) if (test_bit(KEY_VOLUMEDOWN, bitmask))
events.push_back(fd); events.push_back(fd);
else else
close(fd); close(fd);
} }
if (events.empty()) if (events.empty())
return false; return false;
run_finally fin([&]{ std::for_each(events.begin(), events.end(), close); }); run_finally fin([&]{ std::for_each(events.begin(), events.end(), close); });
// Check if volume down key is held continuously for more than 3 seconds // Check if volume down key is held continuously for more than 3 seconds
for (int i = 0; i < 300; ++i) { for (int i = 0; i < 300; ++i) {
bool pressed = false; bool pressed = false;
for (const int &fd : events) { for (const int &fd : events) {
memset(bitmask, 0, sizeof(bitmask)); memset(bitmask, 0, sizeof(bitmask));
ioctl(fd, EVIOCGKEY(sizeof(bitmask)), bitmask); ioctl(fd, EVIOCGKEY(sizeof(bitmask)), bitmask);
if (test_bit(KEY_VOLUMEDOWN, bitmask)) { if (test_bit(KEY_VOLUMEDOWN, bitmask)) {
pressed = true; pressed = true;
break; break;
} }
} }
if (!pressed) if (!pressed)
return false; return false;
// Check every 10ms // Check every 10ms
usleep(10000); usleep(10000);
} }
LOGD("KEY_VOLUMEDOWN detected: enter safe mode\n"); LOGD("KEY_VOLUMEDOWN detected: enter safe mode\n");
return true; return true;
} }
/*********************** /***********************
@@ -275,114 +275,114 @@ static bool check_key_combo() {
static pthread_mutex_t stage_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t stage_lock = PTHREAD_MUTEX_INITIALIZER;
void post_fs_data(int client) { void post_fs_data(int client) {
// ack // ack
write_int(client, 0); write_int(client, 0);
close(client); close(client);
mutex_guard lock(stage_lock); mutex_guard lock(stage_lock);
if (getenv("REMOUNT_ROOT")) if (getenv("REMOUNT_ROOT"))
xmount(nullptr, "/", nullptr, MS_REMOUNT | MS_RDONLY, nullptr); xmount(nullptr, "/", nullptr, MS_REMOUNT | MS_RDONLY, nullptr);
if (!check_data()) if (!check_data())
goto unblock_init; goto unblock_init;
DAEMON_STATE = STATE_POST_FS_DATA; DAEMON_STATE = STATE_POST_FS_DATA;
setup_logfile(true); setup_logfile(true);
LOGI("** post-fs-data mode running\n"); LOGI("** post-fs-data mode running\n");
unlock_blocks(); unlock_blocks();
mount_mirrors(); mount_mirrors();
if (access(SECURE_DIR, F_OK) != 0) { if (access(SECURE_DIR, F_OK) != 0) {
if (SDK_INT < 24) { if (SDK_INT < 24) {
// There is no FBE pre 7.0, we can directly create the folder without issues // There is no FBE pre 7.0, we can directly create the folder without issues
xmkdir(SECURE_DIR, 0700); xmkdir(SECURE_DIR, 0700);
} else { } else {
// If the folder is not automatically created by Android, // If the folder is not automatically created by Android,
// do NOT proceed further. Manual creation of the folder // do NOT proceed further. Manual creation of the folder
// will cause bootloops on FBE devices. // will cause bootloops on FBE devices.
LOGE(SECURE_DIR " is not present, abort\n"); LOGE(SECURE_DIR " is not present, abort\n");
goto early_abort; goto early_abort;
} }
} }
if (!magisk_env()) { if (!magisk_env()) {
LOGE("* Magisk environment incomplete, abort\n"); LOGE("* Magisk environment incomplete, abort\n");
goto early_abort; goto early_abort;
} }
if (getprop("persist.sys.safemode", true) == "1" || check_key_combo()) { if (getprop("persist.sys.safemode", true) == "1" || check_key_combo()) {
safe_mode = true; safe_mode = true;
// Disable all modules and magiskhide so next boot will be clean // Disable all modules and magiskhide so next boot will be clean
disable_modules(); disable_modules();
stop_magiskhide(); stop_magiskhide();
} else { } else {
exec_common_scripts("post-fs-data"); exec_common_scripts("post-fs-data");
auto_start_magiskhide(); auto_start_magiskhide();
handle_modules(); handle_modules();
} }
early_abort: early_abort:
// We still do magic mount because root itself might need it // We still do magic mount because root itself might need it
magic_mount(); magic_mount();
DAEMON_STATE = STATE_POST_FS_DATA_DONE; DAEMON_STATE = STATE_POST_FS_DATA_DONE;
unblock_init: unblock_init:
close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT, 0)); close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT, 0));
} }
void late_start(int client) { void late_start(int client) {
// ack // ack
write_int(client, 0); write_int(client, 0);
close(client); close(client);
mutex_guard lock(stage_lock); mutex_guard lock(stage_lock);
run_finally fin([]{ DAEMON_STATE = STATE_LATE_START_DONE; }); run_finally fin([]{ DAEMON_STATE = STATE_LATE_START_DONE; });
setup_logfile(false); setup_logfile(false);
LOGI("** late_start service mode running\n"); LOGI("** late_start service mode running\n");
if (DAEMON_STATE < STATE_POST_FS_DATA_DONE || safe_mode) if (DAEMON_STATE < STATE_POST_FS_DATA_DONE || safe_mode)
return; return;
exec_common_scripts("service"); exec_common_scripts("service");
exec_module_scripts("service"); exec_module_scripts("service");
} }
void boot_complete(int client) { void boot_complete(int client) {
// ack // ack
write_int(client, 0); write_int(client, 0);
close(client); close(client);
mutex_guard lock(stage_lock); mutex_guard lock(stage_lock);
DAEMON_STATE = STATE_BOOT_COMPLETE; DAEMON_STATE = STATE_BOOT_COMPLETE;
setup_logfile(false); setup_logfile(false);
LOGI("** boot_complete triggered\n"); LOGI("** boot_complete triggered\n");
if (safe_mode) if (safe_mode)
return; return;
// At this point it's safe to create the folder // At this point it's safe to create the folder
if (access(SECURE_DIR, F_OK) != 0) if (access(SECURE_DIR, F_OK) != 0)
xmkdir(SECURE_DIR, 0700); xmkdir(SECURE_DIR, 0700);
auto_start_magiskhide(); auto_start_magiskhide();
if (!check_manager()) { if (!check_manager()) {
if (access(MANAGERAPK, F_OK) == 0) { if (access(MANAGERAPK, F_OK) == 0) {
// Only try to install APK when no manager is installed // Only try to install APK when no manager is installed
// Magisk Manager should be upgraded by itself, not through recovery installs // Magisk Manager should be upgraded by itself, not through recovery installs
rename(MANAGERAPK, "/data/magisk.apk"); rename(MANAGERAPK, "/data/magisk.apk");
install_apk("/data/magisk.apk"); install_apk("/data/magisk.apk");
} else { } else {
// Install stub // Install stub
auto init = MAGISKTMP + "/magiskinit"; auto init = MAGISKTMP + "/magiskinit";
exec_command_sync(init.data(), "-x", "manager", "/data/magisk.apk"); exec_command_sync(init.data(), "-x", "manager", "/data/magisk.apk");
install_apk("/data/magisk.apk"); install_apk("/data/magisk.apk");
} }
} }
unlink(MANAGERAPK); unlink(MANAGERAPK);
} }

View File

@@ -26,101 +26,101 @@ int DAEMON_STATE = STATE_NONE;
static struct stat self_st; static struct stat self_st;
static bool verify_client(pid_t pid) { static bool verify_client(pid_t pid) {
// Verify caller is the same as server // Verify caller is the same as server
char path[32]; char path[32];
sprintf(path, "/proc/%d/exe", pid); sprintf(path, "/proc/%d/exe", pid);
struct stat st; struct stat st;
return !(stat(path, &st) || st.st_dev != self_st.st_dev || st.st_ino != self_st.st_ino); return !(stat(path, &st) || st.st_dev != self_st.st_dev || st.st_ino != self_st.st_ino);
} }
static void request_handler(int client, int req_code, ucred cred) { static void request_handler(int client, int req_code, ucred cred) {
switch (req_code) { switch (req_code) {
case MAGISKHIDE: case MAGISKHIDE:
magiskhide_handler(client); magiskhide_handler(client);
break; break;
case SUPERUSER: case SUPERUSER:
su_daemon_handler(client, &cred); su_daemon_handler(client, &cred);
break; break;
case POST_FS_DATA: case POST_FS_DATA:
post_fs_data(client); post_fs_data(client);
break; break;
case LATE_START: case LATE_START:
late_start(client); late_start(client);
break; break;
case BOOT_COMPLETE: case BOOT_COMPLETE:
boot_complete(client); boot_complete(client);
break; break;
case SQLITE_CMD: case SQLITE_CMD:
exec_sql(client); exec_sql(client);
break; break;
case REMOVE_MODULES: case REMOVE_MODULES:
remove_modules(); remove_modules();
write_int(client, 0); write_int(client, 0);
close(client); close(client);
reboot(); reboot();
break; break;
default: default:
close(client); close(client);
break; break;
} }
} }
static void handle_request(int client) { static void handle_request(int client) {
int req_code; int req_code;
// Verify client credentials // Verify client credentials
ucred cred; ucred cred;
get_client_cred(client, &cred); get_client_cred(client, &cred);
if (cred.uid != 0 && !verify_client(cred.pid)) if (cred.uid != 0 && !verify_client(cred.pid))
goto shortcut; goto shortcut;
req_code = read_int(client); req_code = read_int(client);
if (req_code < 0 || req_code >= DAEMON_CODE_END) if (req_code < 0 || req_code >= DAEMON_CODE_END)
goto shortcut; goto shortcut;
// Check client permissions // Check client permissions
switch (req_code) { switch (req_code) {
case MAGISKHIDE: case MAGISKHIDE:
case POST_FS_DATA: case POST_FS_DATA:
case LATE_START: case LATE_START:
case BOOT_COMPLETE: case BOOT_COMPLETE:
case SQLITE_CMD: case SQLITE_CMD:
case GET_PATH: case GET_PATH:
if (cred.uid != 0) { if (cred.uid != 0) {
write_int(client, ROOT_REQUIRED); write_int(client, ROOT_REQUIRED);
goto shortcut; goto shortcut;
} }
break; break;
case REMOVE_MODULES: case REMOVE_MODULES:
if (cred.uid != UID_SHELL && cred.uid != UID_ROOT) { if (cred.uid != UID_SHELL && cred.uid != UID_ROOT) {
write_int(client, 1); write_int(client, 1);
goto shortcut; goto shortcut;
} }
break; break;
} }
// Simple requests // Simple requests
switch (req_code) { switch (req_code) {
case CHECK_VERSION: case CHECK_VERSION:
write_string(client, MAGISK_VERSION ":MAGISK"); write_string(client, MAGISK_VERSION ":MAGISK");
goto shortcut; goto shortcut;
case CHECK_VERSION_CODE: case CHECK_VERSION_CODE:
write_int(client, MAGISK_VER_CODE); write_int(client, MAGISK_VER_CODE);
goto shortcut; goto shortcut;
case GET_PATH: case GET_PATH:
write_string(client, MAGISKTMP.data()); write_string(client, MAGISKTMP.data());
goto shortcut; goto shortcut;
case START_DAEMON: case START_DAEMON:
setup_logfile(true); setup_logfile(true);
goto shortcut; goto shortcut;
} }
// Create new thread to handle complex requests // Create new thread to handle complex requests
new_daemon_thread([=] { return request_handler(client, req_code, cred); }); new_daemon_thread([=] { return request_handler(client, req_code, cred); });
return; return;
shortcut: shortcut:
close(client); close(client);
} }
static shared_ptr<FILE> log_file; static shared_ptr<FILE> log_file;
@@ -130,185 +130,185 @@ static char *log_buf;
static size_t log_buf_len; static size_t log_buf_len;
void setup_logfile(bool reset) { void setup_logfile(bool reset) {
if (file_backed.test_and_set(memory_order_relaxed)) if (file_backed.test_and_set(memory_order_relaxed))
return; return;
if (reset) if (reset)
rename(LOGFILE, LOGFILE ".bak"); rename(LOGFILE, LOGFILE ".bak");
int fd = xopen(LOGFILE, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0644); int fd = xopen(LOGFILE, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0644);
if (fd < 0) { if (fd < 0) {
log_file.reset(); log_file.reset();
return; return;
} }
// Dump all logs in memory (if exists) // Dump all logs in memory (if exists)
if (log_buf) if (log_buf)
write(fd, log_buf, log_buf_len); write(fd, log_buf, log_buf_len);
if (FILE *fp = fdopen(fd, "a")) { if (FILE *fp = fdopen(fd, "a")) {
setbuf(fp, nullptr); setbuf(fp, nullptr);
log_file.reset(fp, &fclose); log_file.reset(fp, &fclose);
} }
} }
static int magisk_log(int prio, const char *fmt, va_list ap) { static int magisk_log(int prio, const char *fmt, va_list ap) {
va_list args; va_list args;
va_copy(args, ap); va_copy(args, ap);
// Log to logcat // Log to logcat
__android_log_vprint(prio, "Magisk", fmt, ap); __android_log_vprint(prio, "Magisk", fmt, ap);
auto local_log_file = log_file; auto local_log_file = log_file;
if (!local_log_file) if (!local_log_file)
return 0; return 0;
char buf[4096]; char buf[4096];
timeval tv; timeval tv;
tm tm; tm tm;
char type; char type;
switch (prio) { switch (prio) {
case ANDROID_LOG_DEBUG: case ANDROID_LOG_DEBUG:
type = 'D'; type = 'D';
break; break;
case ANDROID_LOG_INFO: case ANDROID_LOG_INFO:
type = 'I'; type = 'I';
break; break;
case ANDROID_LOG_WARN: case ANDROID_LOG_WARN:
type = 'W'; type = 'W';
break; break;
default: default:
type = 'E'; type = 'E';
break; break;
} }
gettimeofday(&tv, nullptr); gettimeofday(&tv, nullptr);
localtime_r(&tv.tv_sec, &tm); localtime_r(&tv.tv_sec, &tm);
size_t len = strftime(buf, sizeof(buf), "%m-%d %T", &tm); size_t len = strftime(buf, sizeof(buf), "%m-%d %T", &tm);
int ms = tv.tv_usec / 1000; int ms = tv.tv_usec / 1000;
len += sprintf(buf + len, ".%03d %c : ", ms, type); len += sprintf(buf + len, ".%03d %c : ", ms, type);
strcpy(buf + len, fmt); strcpy(buf + len, fmt);
return vfprintf(local_log_file.get(), buf, args); return vfprintf(local_log_file.get(), buf, args);
} }
static void android_logging() { static void android_logging() {
auto in_mem_file = make_stream_fp<byte_stream>(log_buf, log_buf_len); auto in_mem_file = make_stream_fp<byte_stream>(log_buf, log_buf_len);
log_file.reset(in_mem_file.release(), [](FILE *) { log_file.reset(in_mem_file.release(), [](FILE *) {
free(log_buf); free(log_buf);
log_buf = nullptr; log_buf = nullptr;
}); });
log_cb.d = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_DEBUG, fmt, ap); }; log_cb.d = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_DEBUG, fmt, ap); };
log_cb.i = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_INFO, fmt, ap); }; log_cb.i = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_INFO, fmt, ap); };
log_cb.w = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_WARN, fmt, ap); }; log_cb.w = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_WARN, fmt, ap); };
log_cb.e = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_ERROR, fmt, ap); }; log_cb.e = [](auto fmt, auto ap){ return magisk_log(ANDROID_LOG_ERROR, fmt, ap); };
log_cb.ex = nop_ex; log_cb.ex = nop_ex;
} }
static void daemon_entry(int ppid) { static void daemon_entry(int ppid) {
android_logging(); android_logging();
int fd = xopen("/dev/null", O_WRONLY); int fd = xopen("/dev/null", O_WRONLY);
xdup2(fd, STDOUT_FILENO); xdup2(fd, STDOUT_FILENO);
xdup2(fd, STDERR_FILENO); xdup2(fd, STDERR_FILENO);
if (fd > STDERR_FILENO) if (fd > STDERR_FILENO)
close(fd); close(fd);
fd = xopen("/dev/zero", O_RDONLY); fd = xopen("/dev/zero", O_RDONLY);
xdup2(fd, STDIN_FILENO); xdup2(fd, STDIN_FILENO);
if (fd > STDERR_FILENO) if (fd > STDERR_FILENO)
close(fd); close(fd);
setsid(); setsid();
setcon("u:r:" SEPOL_PROC_DOMAIN ":s0"); setcon("u:r:" SEPOL_PROC_DOMAIN ":s0");
LOGI(NAME_WITH_VER(Magisk) " daemon started\n"); LOGI(NAME_WITH_VER(Magisk) " daemon started\n");
// Make sure ppid is not in acct // Make sure ppid is not in acct
char src[64], dest[64]; char src[64], dest[64];
sprintf(src, "/acct/uid_0/pid_%d", ppid); sprintf(src, "/acct/uid_0/pid_%d", ppid);
sprintf(dest, "/acct/uid_0/pid_%d", getpid()); sprintf(dest, "/acct/uid_0/pid_%d", getpid());
rename(src, dest); rename(src, dest);
// Get self stat // Get self stat
xreadlink("/proc/self/exe", src, sizeof(src)); xreadlink("/proc/self/exe", src, sizeof(src));
MAGISKTMP = dirname(src); MAGISKTMP = dirname(src);
xstat("/proc/self/exe", &self_st); xstat("/proc/self/exe", &self_st);
// Get API level // Get API level
parse_prop_file("/system/build.prop", [](auto key, auto val) -> bool { parse_prop_file("/system/build.prop", [](auto key, auto val) -> bool {
if (key == "ro.build.version.sdk") { if (key == "ro.build.version.sdk") {
SDK_INT = parse_int(val); SDK_INT = parse_int(val);
return false; return false;
} }
return true; return true;
}); });
if (SDK_INT < 0) { if (SDK_INT < 0) {
// In case some devices do not store this info in build.prop, fallback to getprop // In case some devices do not store this info in build.prop, fallback to getprop
auto sdk = getprop("ro.build.version.sdk"); auto sdk = getprop("ro.build.version.sdk");
if (!sdk.empty()) { if (!sdk.empty()) {
SDK_INT = parse_int(sdk); SDK_INT = parse_int(sdk);
} }
} }
LOGI("* Device API level: %d\n", SDK_INT); LOGI("* Device API level: %d\n", SDK_INT);
restore_tmpcon(); restore_tmpcon();
// SAR cleanups // SAR cleanups
auto mount_list = MAGISKTMP + "/" ROOTMNT; auto mount_list = MAGISKTMP + "/" ROOTMNT;
if (access(mount_list.data(), F_OK) == 0) { if (access(mount_list.data(), F_OK) == 0) {
file_readline(true, mount_list.data(), [](string_view line) -> bool { file_readline(true, mount_list.data(), [](string_view line) -> bool {
umount2(line.data(), MNT_DETACH); umount2(line.data(), MNT_DETACH);
return true; return true;
}); });
} }
unlink("/dev/.se"); unlink("/dev/.se");
// Load config status // Load config status
auto config = MAGISKTMP + "/" INTLROOT "/config"; auto config = MAGISKTMP + "/" INTLROOT "/config";
parse_prop_file(config.data(), [](auto key, auto val) -> bool { parse_prop_file(config.data(), [](auto key, auto val) -> bool {
if (key == "RECOVERYMODE" && val == "true") if (key == "RECOVERYMODE" && val == "true")
RECOVERY_MODE = true; RECOVERY_MODE = true;
return true; return true;
}); });
struct sockaddr_un sun; struct sockaddr_un sun;
socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET); socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET);
fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (xbind(fd, (struct sockaddr*) &sun, len)) if (xbind(fd, (struct sockaddr*) &sun, len))
exit(1); exit(1);
xlisten(fd, 10); xlisten(fd, 10);
// Change process name // Change process name
set_nice_name("magiskd"); set_nice_name("magiskd");
// Block all signals // Block all signals
sigset_t block_set; sigset_t block_set;
sigfillset(&block_set); sigfillset(&block_set);
pthread_sigmask(SIG_SETMASK, &block_set, nullptr); pthread_sigmask(SIG_SETMASK, &block_set, nullptr);
// Loop forever to listen for requests // Loop forever to listen for requests
for (;;) { for (;;) {
int client = xaccept4(fd, nullptr, nullptr, SOCK_CLOEXEC); int client = xaccept4(fd, nullptr, nullptr, SOCK_CLOEXEC);
handle_request(client); handle_request(client);
} }
} }
int connect_daemon(bool create) { int connect_daemon(bool create) {
struct sockaddr_un sun; struct sockaddr_un sun;
socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET); socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET);
int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (connect(fd, (struct sockaddr*) &sun, len)) { if (connect(fd, (struct sockaddr*) &sun, len)) {
if (!create || getuid() != UID_ROOT || getgid() != UID_ROOT) { if (!create || getuid() != UID_ROOT || getgid() != UID_ROOT) {
LOGE("No daemon is currently running!\n"); LOGE("No daemon is currently running!\n");
exit(1); exit(1);
} }
int ppid = getpid(); int ppid = getpid();
LOGD("client: launching new main daemon process\n"); LOGD("client: launching new main daemon process\n");
if (fork_dont_care() == 0) { if (fork_dont_care() == 0) {
close(fd); close(fd);
daemon_entry(ppid); daemon_entry(ppid);
} }
while (connect(fd, (struct sockaddr*) &sun, len)) while (connect(fd, (struct sockaddr*) &sun, len))
usleep(10000); usleep(10000);
} }
return fd; return fd;
} }

View File

@@ -26,19 +26,19 @@ static sqlite3 *mDB = nullptr;
#define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */
static int (*sqlite3_open_v2)( static int (*sqlite3_open_v2)(
const char *filename, const char *filename,
sqlite3 **ppDb, sqlite3 **ppDb,
int flags, int flags,
const char *zVfs); const char *zVfs);
static const char *(*sqlite3_errmsg)(sqlite3 *db); static const char *(*sqlite3_errmsg)(sqlite3 *db);
static int (*sqlite3_close)(sqlite3 *db); static int (*sqlite3_close)(sqlite3 *db);
static void (*sqlite3_free)(void *v); static void (*sqlite3_free)(void *v);
static int (*sqlite3_exec)( static int (*sqlite3_exec)(
sqlite3 *db, sqlite3 *db,
const char *sql, const char *sql,
int (*callback)(void*, int, char**, char**), int (*callback)(void*, int, char**, char**),
void *v, void *v,
char **errmsg); char **errmsg);
// Internal Android linker APIs // Internal Android linker APIs
@@ -46,14 +46,14 @@ static void (*android_get_LD_LIBRARY_PATH)(char *buffer, size_t buffer_size);
static void (*android_update_LD_LIBRARY_PATH)(const char *ld_library_path); static void (*android_update_LD_LIBRARY_PATH)(const char *ld_library_path);
#define DLERR(ptr) if (!(ptr)) { \ #define DLERR(ptr) if (!(ptr)) { \
LOGE("db: %s\n", dlerror()); \ LOGE("db: %s\n", dlerror()); \
return false; \ return false; \
} }
#define DLOAD(handle, arg) {\ #define DLOAD(handle, arg) {\
auto f = dlsym(handle, #arg); \ auto f = dlsym(handle, #arg); \
DLERR(f) \ DLERR(f) \
*(void **) &(arg) = f; \ *(void **) &(arg) = f; \
} }
#ifdef __LP64__ #ifdef __LP64__
@@ -65,339 +65,339 @@ constexpr char apex_path[] = "/apex/com.android.runtime/lib:/apex/com.android.ar
static int dl_init = 0; static int dl_init = 0;
static bool dload_sqlite() { static bool dload_sqlite() {
if (dl_init) if (dl_init)
return dl_init > 0; return dl_init > 0;
dl_init = -1; dl_init = -1;
auto sqlite = dlopen("libsqlite.so", RTLD_LAZY); auto sqlite = dlopen("libsqlite.so", RTLD_LAZY);
if (!sqlite) { if (!sqlite) {
// Should only happen on Android 10+ // Should only happen on Android 10+
auto dl = dlopen("libdl_android.so", RTLD_LAZY); auto dl = dlopen("libdl_android.so", RTLD_LAZY);
DLERR(dl); DLERR(dl);
DLOAD(dl, android_get_LD_LIBRARY_PATH); DLOAD(dl, android_get_LD_LIBRARY_PATH);
DLOAD(dl, android_update_LD_LIBRARY_PATH); DLOAD(dl, android_update_LD_LIBRARY_PATH);
// Inject APEX into LD_LIBRARY_PATH // Inject APEX into LD_LIBRARY_PATH
char ld_path[4096]; char ld_path[4096];
memcpy(ld_path, apex_path, sizeof(apex_path)); memcpy(ld_path, apex_path, sizeof(apex_path));
constexpr int len = sizeof(apex_path) - 1; constexpr int len = sizeof(apex_path) - 1;
android_get_LD_LIBRARY_PATH(ld_path + len, sizeof(ld_path) - len); android_get_LD_LIBRARY_PATH(ld_path + len, sizeof(ld_path) - len);
android_update_LD_LIBRARY_PATH(ld_path); android_update_LD_LIBRARY_PATH(ld_path);
sqlite = dlopen("libsqlite.so", RTLD_LAZY); sqlite = dlopen("libsqlite.so", RTLD_LAZY);
// Revert LD_LIBRARY_PATH just in case // Revert LD_LIBRARY_PATH just in case
android_update_LD_LIBRARY_PATH(ld_path + len); android_update_LD_LIBRARY_PATH(ld_path + len);
} }
DLERR(sqlite); DLERR(sqlite);
DLOAD(sqlite, sqlite3_open_v2); DLOAD(sqlite, sqlite3_open_v2);
DLOAD(sqlite, sqlite3_errmsg); DLOAD(sqlite, sqlite3_errmsg);
DLOAD(sqlite, sqlite3_close); DLOAD(sqlite, sqlite3_close);
DLOAD(sqlite, sqlite3_exec); DLOAD(sqlite, sqlite3_exec);
DLOAD(sqlite, sqlite3_free); DLOAD(sqlite, sqlite3_free);
dl_init = 1; dl_init = 1;
return true; return true;
} }
int db_strings::getKeyIdx(string_view key) const { int db_strings::getKeyIdx(string_view key) const {
int idx = DB_STRING_NUM; int idx = DB_STRING_NUM;
for (int i = 0; i < DB_STRING_NUM; ++i) { for (int i = 0; i < DB_STRING_NUM; ++i) {
if (key == DB_STRING_KEYS[i]) if (key == DB_STRING_KEYS[i])
idx = i; idx = i;
} }
return idx; return idx;
} }
db_settings::db_settings() { db_settings::db_settings() {
// Default settings // Default settings
data[ROOT_ACCESS] = ROOT_ACCESS_APPS_AND_ADB; data[ROOT_ACCESS] = ROOT_ACCESS_APPS_AND_ADB;
data[SU_MULTIUSER_MODE] = MULTIUSER_MODE_OWNER_ONLY; data[SU_MULTIUSER_MODE] = MULTIUSER_MODE_OWNER_ONLY;
data[SU_MNT_NS] = NAMESPACE_MODE_REQUESTER; data[SU_MNT_NS] = NAMESPACE_MODE_REQUESTER;
data[HIDE_CONFIG] = false; data[HIDE_CONFIG] = false;
} }
int db_settings::getKeyIdx(string_view key) const { int db_settings::getKeyIdx(string_view key) const {
int idx = DB_SETTINGS_NUM; int idx = DB_SETTINGS_NUM;
for (int i = 0; i < DB_SETTINGS_NUM; ++i) { for (int i = 0; i < DB_SETTINGS_NUM; ++i) {
if (key == DB_SETTING_KEYS[i]) if (key == DB_SETTING_KEYS[i])
idx = i; idx = i;
} }
return idx; return idx;
} }
static int ver_cb(void *ver, int, char **data, char **) { static int ver_cb(void *ver, int, char **data, char **) {
*((int *) ver) = parse_int(data[0]); *((int *) ver) = parse_int(data[0]);
return 0; return 0;
} }
#define err_ret(e) if (e) return e; #define err_ret(e) if (e) return e;
static char *open_and_init_db(sqlite3 *&db) { static char *open_and_init_db(sqlite3 *&db) {
if (!dload_sqlite()) if (!dload_sqlite())
return strdup("Cannot load libsqlite.so"); return strdup("Cannot load libsqlite.so");
int ret = sqlite3_open_v2(MAGISKDB, &db, int ret = sqlite3_open_v2(MAGISKDB, &db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, nullptr); SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, nullptr);
if (ret) if (ret)
return strdup(sqlite3_errmsg(db)); return strdup(sqlite3_errmsg(db));
int ver; int ver;
bool upgrade = false; bool upgrade = false;
char *err; char *err;
sqlite3_exec(db, "PRAGMA user_version", ver_cb, &ver, &err); sqlite3_exec(db, "PRAGMA user_version", ver_cb, &ver, &err);
err_ret(err); err_ret(err);
if (ver > DB_VERSION) { if (ver > DB_VERSION) {
// Don't support downgrading database // Don't support downgrading database
sqlite3_close(db); sqlite3_close(db);
return nullptr; return nullptr;
} }
if (ver < 3) { if (ver < 3) {
// Policies // Policies
sqlite3_exec(db, sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS policies " "CREATE TABLE IF NOT EXISTS policies "
"(uid INT, package_name TEXT, policy INT, until INT, " "(uid INT, package_name TEXT, policy INT, until INT, "
"logging INT, notification INT, PRIMARY KEY(uid))", "logging INT, notification INT, PRIMARY KEY(uid))",
nullptr, nullptr, &err); nullptr, nullptr, &err);
err_ret(err); err_ret(err);
// Settings // Settings
sqlite3_exec(db, sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS settings " "CREATE TABLE IF NOT EXISTS settings "
"(key TEXT, value INT, PRIMARY KEY(key))", "(key TEXT, value INT, PRIMARY KEY(key))",
nullptr, nullptr, &err); nullptr, nullptr, &err);
err_ret(err); err_ret(err);
ver = 3; ver = 3;
upgrade = true; upgrade = true;
} }
if (ver < 4) { if (ver < 4) {
// Strings // Strings
sqlite3_exec(db, sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS strings " "CREATE TABLE IF NOT EXISTS strings "
"(key TEXT, value TEXT, PRIMARY KEY(key))", "(key TEXT, value TEXT, PRIMARY KEY(key))",
nullptr, nullptr, &err); nullptr, nullptr, &err);
err_ret(err); err_ret(err);
ver = 4; ver = 4;
upgrade = true; upgrade = true;
} }
if (ver < 5) { if (ver < 5) {
sqlite3_exec(db, "UPDATE policies SET uid=uid%100000", nullptr, nullptr, &err); sqlite3_exec(db, "UPDATE policies SET uid=uid%100000", nullptr, nullptr, &err);
err_ret(err); err_ret(err);
/* Directly jump to version 6 */ /* Directly jump to version 6 */
ver = 6; ver = 6;
upgrade = true; upgrade = true;
} }
if (ver < 7) { if (ver < 7) {
sqlite3_exec(db, sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS hidelist " "CREATE TABLE IF NOT EXISTS hidelist "
"(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process));", "(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process));",
nullptr, nullptr, &err); nullptr, nullptr, &err);
err_ret(err); err_ret(err);
/* Directly jump to version 9 */ /* Directly jump to version 9 */
ver = 9; ver = 9;
upgrade = true; upgrade = true;
} }
if (ver < 8) { if (ver < 8) {
sqlite3_exec(db, sqlite3_exec(db,
"BEGIN TRANSACTION;" "BEGIN TRANSACTION;"
"ALTER TABLE hidelist RENAME TO hidelist_tmp;" "ALTER TABLE hidelist RENAME TO hidelist_tmp;"
"CREATE TABLE IF NOT EXISTS hidelist " "CREATE TABLE IF NOT EXISTS hidelist "
"(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process));" "(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process));"
"INSERT INTO hidelist SELECT process as package_name, process FROM hidelist_tmp;" "INSERT INTO hidelist SELECT process as package_name, process FROM hidelist_tmp;"
"DROP TABLE hidelist_tmp;" "DROP TABLE hidelist_tmp;"
"COMMIT;", "COMMIT;",
nullptr, nullptr, &err); nullptr, nullptr, &err);
err_ret(err); err_ret(err);
/* Directly jump to version 9 */ /* Directly jump to version 9 */
ver = 9; ver = 9;
upgrade = true; upgrade = true;
} }
if (ver < 9) { if (ver < 9) {
sqlite3_exec(db, sqlite3_exec(db,
"BEGIN TRANSACTION;" "BEGIN TRANSACTION;"
"ALTER TABLE hidelist RENAME TO hidelist_tmp;" "ALTER TABLE hidelist RENAME TO hidelist_tmp;"
"CREATE TABLE IF NOT EXISTS hidelist " "CREATE TABLE IF NOT EXISTS hidelist "
"(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process));" "(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process));"
"INSERT INTO hidelist SELECT * FROM hidelist_tmp;" "INSERT INTO hidelist SELECT * FROM hidelist_tmp;"
"DROP TABLE hidelist_tmp;" "DROP TABLE hidelist_tmp;"
"COMMIT;", "COMMIT;",
nullptr, nullptr, &err); nullptr, nullptr, &err);
err_ret(err); err_ret(err);
ver = 9; ver = 9;
upgrade = true; upgrade = true;
} }
if (ver < 10) { if (ver < 10) {
sqlite3_exec(db, "DROP TABLE IF EXISTS logs", nullptr, nullptr, &err); sqlite3_exec(db, "DROP TABLE IF EXISTS logs", nullptr, nullptr, &err);
err_ret(err); err_ret(err);
ver = 10; ver = 10;
upgrade = true; upgrade = true;
} }
if (upgrade) { if (upgrade) {
// Set version // Set version
char query[32]; char query[32];
sprintf(query, "PRAGMA user_version=%d", ver); sprintf(query, "PRAGMA user_version=%d", ver);
sqlite3_exec(db, query, nullptr, nullptr, &err); sqlite3_exec(db, query, nullptr, nullptr, &err);
err_ret(err); err_ret(err);
} }
return nullptr; return nullptr;
} }
char *db_exec(const char *sql) { char *db_exec(const char *sql) {
char *err; char *err;
if (mDB == nullptr) { if (mDB == nullptr) {
err = open_and_init_db(mDB); err = open_and_init_db(mDB);
db_err_cmd(err, db_err_cmd(err,
// Open fails, remove and reconstruct // Open fails, remove and reconstruct
unlink(MAGISKDB); unlink(MAGISKDB);
err = open_and_init_db(mDB); err = open_and_init_db(mDB);
err_ret(err); err_ret(err);
); );
} }
if (mDB) { if (mDB) {
sqlite3_exec(mDB, sql, nullptr, nullptr, &err); sqlite3_exec(mDB, sql, nullptr, nullptr, &err);
return err; return err;
} }
return nullptr; return nullptr;
} }
char *db_exec(const char *sql, const db_row_cb &fn) { char *db_exec(const char *sql, const db_row_cb &fn) {
char *err; char *err;
if (mDB == nullptr) { if (mDB == nullptr) {
err = open_and_init_db(mDB); err = open_and_init_db(mDB);
db_err_cmd(err, db_err_cmd(err,
// Open fails, remove and reconstruct // Open fails, remove and reconstruct
unlink(MAGISKDB); unlink(MAGISKDB);
err = open_and_init_db(mDB); err = open_and_init_db(mDB);
err_ret(err); err_ret(err);
); );
} }
if (mDB) { if (mDB) {
sqlite3_exec(mDB, sql, [](void *cb, int col_num, char **data, char **col_name) -> int { sqlite3_exec(mDB, sql, [](void *cb, int col_num, char **data, char **col_name) -> int {
auto &func = *reinterpret_cast<const db_row_cb*>(cb); auto &func = *reinterpret_cast<const db_row_cb*>(cb);
db_row row; db_row row;
for (int i = 0; i < col_num; ++i) for (int i = 0; i < col_num; ++i)
row[col_name[i]] = data[i]; row[col_name[i]] = data[i];
return func(row) ? 0 : 1; return func(row) ? 0 : 1;
}, (void *) &fn, &err); }, (void *) &fn, &err);
return err; return err;
} }
return nullptr; return nullptr;
} }
int get_db_settings(db_settings &cfg, int key) { int get_db_settings(db_settings &cfg, int key) {
char *err; char *err;
auto settings_cb = [&](db_row &row) -> bool { auto settings_cb = [&](db_row &row) -> bool {
cfg[row["key"]] = parse_int(row["value"]); cfg[row["key"]] = parse_int(row["value"]);
LOGD("magiskdb: query %s=[%s]\n", row["key"].data(), row["value"].data()); LOGD("magiskdb: query %s=[%s]\n", row["key"].data(), row["value"].data());
return true; return true;
}; };
if (key >= 0) { if (key >= 0) {
char query[128]; char query[128];
sprintf(query, "SELECT key, value FROM settings WHERE key='%s'", DB_SETTING_KEYS[key]); sprintf(query, "SELECT key, value FROM settings WHERE key='%s'", DB_SETTING_KEYS[key]);
err = db_exec(query, settings_cb); err = db_exec(query, settings_cb);
} else { } else {
err = db_exec("SELECT key, value FROM settings", settings_cb); err = db_exec("SELECT key, value FROM settings", settings_cb);
} }
db_err_cmd(err, return 1); db_err_cmd(err, return 1);
return 0; return 0;
} }
int get_db_strings(db_strings &str, int key) { int get_db_strings(db_strings &str, int key) {
char *err; char *err;
auto string_cb = [&](db_row &row) -> bool { auto string_cb = [&](db_row &row) -> bool {
str[row["key"]] = row["value"]; str[row["key"]] = row["value"];
return true; return true;
}; };
if (key >= 0) { if (key >= 0) {
char query[128]; char query[128];
sprintf(query, "SELECT key, value FROM strings WHERE key='%s'", DB_STRING_KEYS[key]); sprintf(query, "SELECT key, value FROM strings WHERE key='%s'", DB_STRING_KEYS[key]);
err = db_exec(query, string_cb); err = db_exec(query, string_cb);
} else { } else {
err = db_exec("SELECT key, value FROM strings", string_cb); err = db_exec("SELECT key, value FROM strings", string_cb);
} }
db_err_cmd(err, return 1); db_err_cmd(err, return 1);
return 0; return 0;
} }
int get_uid_policy(su_access &su, int uid) { int get_uid_policy(su_access &su, int uid) {
char query[256], *err; char query[256], *err;
sprintf(query, "SELECT policy, logging, notification FROM policies " sprintf(query, "SELECT policy, logging, notification FROM policies "
"WHERE uid=%d AND (until=0 OR until>%li)", uid, time(nullptr)); "WHERE uid=%d AND (until=0 OR until>%li)", uid, time(nullptr));
err = db_exec(query, [&](db_row &row) -> bool { err = db_exec(query, [&](db_row &row) -> bool {
su.policy = (policy_t) parse_int(row["policy"]); su.policy = (policy_t) parse_int(row["policy"]);
su.log = parse_int(row["logging"]); su.log = parse_int(row["logging"]);
su.notify = parse_int(row["notification"]); su.notify = parse_int(row["notification"]);
LOGD("magiskdb: query policy=[%d] log=[%d] notify=[%d]\n", su.policy, su.log, su.notify); LOGD("magiskdb: query policy=[%d] log=[%d] notify=[%d]\n", su.policy, su.log, su.notify);
return true; return true;
}); });
db_err_cmd(err, return 1); db_err_cmd(err, return 1);
return 0; return 0;
} }
bool check_manager(string *pkg) { bool check_manager(string *pkg) {
db_strings str; db_strings str;
get_db_strings(str, SU_MANAGER); get_db_strings(str, SU_MANAGER);
bool ret = validate_manager(str[SU_MANAGER], 0, nullptr); bool ret = validate_manager(str[SU_MANAGER], 0, nullptr);
if (pkg) { if (pkg) {
if (ret) if (ret)
pkg->swap(str[SU_MANAGER]); pkg->swap(str[SU_MANAGER]);
else else
*pkg = "xxx"; /* Make sure the return pkg can never exist */ *pkg = "xxx"; /* Make sure the return pkg can never exist */
} }
return ret; return ret;
} }
bool validate_manager(string &pkg, int userid, struct stat *st) { bool validate_manager(string &pkg, int userid, struct stat *st) {
struct stat tmp_st; struct stat tmp_st;
if (st == nullptr) if (st == nullptr)
st = &tmp_st; st = &tmp_st;
// Prefer DE storage // Prefer DE storage
char app_path[128]; char app_path[128];
sprintf(app_path, "%s/%d/%s", APP_DATA_DIR, userid, pkg.data()); sprintf(app_path, "%s/%d/%s", APP_DATA_DIR, userid, pkg.data());
if (pkg.empty() || stat(app_path, st)) { if (pkg.empty() || stat(app_path, st)) {
// Check the official package name // Check the official package name
sprintf(app_path, "%s/%d/" JAVA_PACKAGE_NAME, APP_DATA_DIR, userid); sprintf(app_path, "%s/%d/" JAVA_PACKAGE_NAME, APP_DATA_DIR, userid);
if (stat(app_path, st)) { if (stat(app_path, st)) {
LOGE("su: cannot find manager\n"); LOGE("su: cannot find manager\n");
memset(st, 0, sizeof(*st)); memset(st, 0, sizeof(*st));
pkg.clear(); pkg.clear();
return false; return false;
} else { } else {
// Switch to official package if exists // Switch to official package if exists
pkg = JAVA_PACKAGE_NAME; pkg = JAVA_PACKAGE_NAME;
} }
} }
return true; return true;
} }
void exec_sql(int client) { void exec_sql(int client) {
run_finally f([=]{ close(client); }); run_finally f([=]{ close(client); });
char *sql = read_string(client); char *sql = read_string(client);
char *err = db_exec(sql, [&](db_row &row) -> bool { char *err = db_exec(sql, [&](db_row &row) -> bool {
string out; string out;
bool first = true; bool first = true;
for (auto it : row) { for (auto it : row) {
if (first) first = false; if (first) first = false;
else out += '|'; else out += '|';
out += it.first; out += it.first;
out += '='; out += '=';
out += it.second; out += it.second;
} }
write_int(client, out.length()); write_int(client, out.length());
xwrite(client, out.data(), out.length()); xwrite(client, out.data(), out.length());
return true; return true;
}); });
free(sql); free(sql);
write_int(client, 0); write_int(client, 0);
db_err_cmd(err, return; ); db_err_cmd(err, return; );
} }
bool db_err(char *e) { bool db_err(char *e) {
if (e) { if (e) {
LOGE("sqlite3_exec: %s\n", e); LOGE("sqlite3_exec: %s\n", e);
sqlite3_free(e); sqlite3_free(e);
return true; return true;
} }
return false; return false;
} }

View File

@@ -10,7 +10,7 @@
using namespace std; using namespace std;
[[noreturn]] static void usage() { [[noreturn]] static void usage() {
fprintf(stderr, fprintf(stderr,
R"EOF(Magisk - Multi-purpose Utility R"EOF(Magisk - Multi-purpose Utility
Usage: magisk [applet [arguments]...] Usage: magisk [applet [arguments]...]
@@ -39,92 +39,92 @@ Advanced Options (Internal APIs):
Available applets: Available applets:
)EOF"); )EOF");
for (int i = 0; applet_names[i]; ++i) for (int i = 0; applet_names[i]; ++i)
fprintf(stderr, i ? ", %s" : " %s", applet_names[i]); fprintf(stderr, i ? ", %s" : " %s", applet_names[i]);
fprintf(stderr, "\n\n"); fprintf(stderr, "\n\n");
exit(1); exit(1);
} }
int magisk_main(int argc, char *argv[]) { int magisk_main(int argc, char *argv[]) {
if (argc < 2) if (argc < 2)
usage(); usage();
if (argv[1] == "-c"sv) { if (argv[1] == "-c"sv) {
printf(MAGISK_VERSION ":MAGISK (" str(MAGISK_VER_CODE) ")\n"); printf(MAGISK_VERSION ":MAGISK (" str(MAGISK_VER_CODE) ")\n");
return 0; return 0;
} else if (argv[1] == "-v"sv) { } else if (argv[1] == "-v"sv) {
int fd = connect_daemon(); int fd = connect_daemon();
write_int(fd, CHECK_VERSION); write_int(fd, CHECK_VERSION);
char *v = read_string(fd); char *v = read_string(fd);
printf("%s\n", v); printf("%s\n", v);
free(v); free(v);
return 0; return 0;
} else if (argv[1] == "-V"sv) { } else if (argv[1] == "-V"sv) {
int fd = connect_daemon(); int fd = connect_daemon();
write_int(fd, CHECK_VERSION_CODE); write_int(fd, CHECK_VERSION_CODE);
printf("%d\n", read_int(fd)); printf("%d\n", read_int(fd));
return 0; return 0;
} else if (argv[1] == "--list"sv) { } else if (argv[1] == "--list"sv) {
for (int i = 0; applet_names[i]; ++i) for (int i = 0; applet_names[i]; ++i)
printf("%s\n", applet_names[i]); printf("%s\n", applet_names[i]);
return 0; return 0;
} else if (argv[1] == "--unlock-blocks"sv) { } else if (argv[1] == "--unlock-blocks"sv) {
unlock_blocks(); unlock_blocks();
return 0; return 0;
} else if (argv[1] == "--restorecon"sv) { } else if (argv[1] == "--restorecon"sv) {
restorecon(); restorecon();
return 0; return 0;
} else if (argc >= 4 && argv[1] == "--clone-attr"sv) {; } else if (argc >= 4 && argv[1] == "--clone-attr"sv) {;
clone_attr(argv[2], argv[3]); clone_attr(argv[2], argv[3]);
return 0; return 0;
} else if (argc >= 4 && argv[1] == "--clone"sv) { } else if (argc >= 4 && argv[1] == "--clone"sv) {
cp_afc(argv[2], argv[3]); cp_afc(argv[2], argv[3]);
return 0; return 0;
} else if (argv[1] == "--daemon"sv) { } else if (argv[1] == "--daemon"sv) {
int fd = connect_daemon(true); int fd = connect_daemon(true);
write_int(fd, START_DAEMON); write_int(fd, START_DAEMON);
return 0; return 0;
} else if (argv[1] == "--post-fs-data"sv) { } else if (argv[1] == "--post-fs-data"sv) {
int fd = connect_daemon(true); int fd = connect_daemon(true);
write_int(fd, POST_FS_DATA); write_int(fd, POST_FS_DATA);
return read_int(fd); return read_int(fd);
} else if (argv[1] == "--service"sv) { } else if (argv[1] == "--service"sv) {
int fd = connect_daemon(true); int fd = connect_daemon(true);
write_int(fd, LATE_START); write_int(fd, LATE_START);
return read_int(fd); return read_int(fd);
} else if (argv[1] == "--boot-complete"sv) { } else if (argv[1] == "--boot-complete"sv) {
int fd = connect_daemon(true); int fd = connect_daemon(true);
write_int(fd, BOOT_COMPLETE); write_int(fd, BOOT_COMPLETE);
return read_int(fd); return read_int(fd);
} else if (argc >= 3 && argv[1] == "--sqlite"sv) { } else if (argc >= 3 && argv[1] == "--sqlite"sv) {
int fd = connect_daemon(); int fd = connect_daemon();
write_int(fd, SQLITE_CMD); write_int(fd, SQLITE_CMD);
write_string(fd, argv[2]); write_string(fd, argv[2]);
for (;;) { for (;;) {
char *res = read_string(fd); char *res = read_string(fd);
if (res[0] == '\0') { if (res[0] == '\0') {
return 0; return 0;
} }
printf("%s\n", res); printf("%s\n", res);
free(res); free(res);
} }
} else if (argv[1] == "--remove-modules"sv) { } else if (argv[1] == "--remove-modules"sv) {
int fd = connect_daemon(); int fd = connect_daemon();
write_int(fd, REMOVE_MODULES); write_int(fd, REMOVE_MODULES);
return read_int(fd); return read_int(fd);
} else if (argv[1] == "--path"sv) { } else if (argv[1] == "--path"sv) {
int fd = connect_daemon(); int fd = connect_daemon();
write_int(fd, GET_PATH); write_int(fd, GET_PATH);
char *path = read_string(fd); char *path = read_string(fd);
printf("%s\n", path); printf("%s\n", path);
return 0; return 0;
} else if (argc >= 3 && argv[1] == "--install-module"sv) { } else if (argc >= 3 && argv[1] == "--install-module"sv) {
install_module(argv[2]); install_module(argv[2]);
} }
#if 0 #if 0
/* Entry point for testing stuffs */ /* Entry point for testing stuffs */
else if (argv[1] == "--test"sv) { else if (argv[1] == "--test"sv) {
return 0; return 0;
} }
#endif #endif
usage(); usage();
} }

File diff suppressed because it is too large Load Diff

View File

@@ -15,89 +15,89 @@ using namespace std;
#define EXEC_CON "u:object_r:" SEPOL_EXEC_TYPE ":s0" #define EXEC_CON "u:object_r:" SEPOL_EXEC_TYPE ":s0"
static void restore_syscon(int dirfd) { static void restore_syscon(int dirfd) {
struct dirent *entry; struct dirent *entry;
DIR *dir; DIR *dir;
char *con; char *con;
fgetfilecon(dirfd, &con); fgetfilecon(dirfd, &con);
if (strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0) if (strlen(con) == 0 || strcmp(con, UNLABEL_CON) == 0)
fsetfilecon(dirfd, SYSTEM_CON); fsetfilecon(dirfd, SYSTEM_CON);
freecon(con); freecon(con);
dir = xfdopendir(dirfd); dir = xfdopendir(dirfd);
while ((entry = xreaddir(dir))) { while ((entry = xreaddir(dir))) {
int fd = openat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC); int fd = openat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
if (entry->d_type == DT_DIR) { if (entry->d_type == DT_DIR) {
restore_syscon(fd); restore_syscon(fd);
} else if (entry->d_type == DT_REG) { } else if (entry->d_type == DT_REG) {
fgetfilecon(fd, &con); fgetfilecon(fd, &con);
if (con[0] == '\0' || strcmp(con, UNLABEL_CON) == 0) if (con[0] == '\0' || strcmp(con, UNLABEL_CON) == 0)
fsetfilecon(fd, SYSTEM_CON); fsetfilecon(fd, SYSTEM_CON);
freecon(con); freecon(con);
} else if (entry->d_type == DT_LNK) { } else if (entry->d_type == DT_LNK) {
getfilecon_at(dirfd, entry->d_name, &con); getfilecon_at(dirfd, entry->d_name, &con);
if (con[0] == '\0' || strcmp(con, UNLABEL_CON) == 0) if (con[0] == '\0' || strcmp(con, UNLABEL_CON) == 0)
setfilecon_at(dirfd, entry->d_name, con); setfilecon_at(dirfd, entry->d_name, con);
freecon(con); freecon(con);
} }
close(fd); close(fd);
} }
} }
static void restore_magiskcon(int dirfd) { static void restore_magiskcon(int dirfd) {
struct dirent *entry; struct dirent *entry;
DIR *dir; DIR *dir;
fsetfilecon(dirfd, MAGISK_CON); fsetfilecon(dirfd, MAGISK_CON);
fchown(dirfd, 0, 0); fchown(dirfd, 0, 0);
dir = xfdopendir(dirfd); dir = xfdopendir(dirfd);
while ((entry = xreaddir(dir))) { while ((entry = xreaddir(dir))) {
int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC); int fd = xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC);
if (entry->d_type == DT_DIR) { if (entry->d_type == DT_DIR) {
restore_magiskcon(fd); restore_magiskcon(fd);
} else if (entry->d_type) { } else if (entry->d_type) {
fsetfilecon(fd, MAGISK_CON); fsetfilecon(fd, MAGISK_CON);
fchown(fd, 0, 0); fchown(fd, 0, 0);
} }
close(fd); close(fd);
} }
} }
void restorecon() { void restorecon() {
int fd = xopen(SELINUX_CONTEXT, O_WRONLY | O_CLOEXEC); int fd = xopen(SELINUX_CONTEXT, O_WRONLY | O_CLOEXEC);
if (write(fd, ADB_CON, sizeof(ADB_CON)) >= 0) if (write(fd, ADB_CON, sizeof(ADB_CON)) >= 0)
lsetfilecon(SECURE_DIR, ADB_CON); lsetfilecon(SECURE_DIR, ADB_CON);
close(fd); close(fd);
lsetfilecon(MODULEROOT, SYSTEM_CON); lsetfilecon(MODULEROOT, SYSTEM_CON);
fd = xopen(MODULEROOT, O_RDONLY | O_CLOEXEC); fd = xopen(MODULEROOT, O_RDONLY | O_CLOEXEC);
restore_syscon(fd); restore_syscon(fd);
close(fd); close(fd);
fd = xopen(DATABIN, O_RDONLY | O_CLOEXEC); fd = xopen(DATABIN, O_RDONLY | O_CLOEXEC);
restore_magiskcon(fd); restore_magiskcon(fd);
close(fd); close(fd);
} }
void restore_tmpcon() { void restore_tmpcon() {
if (MAGISKTMP == "/system/bin") { if (MAGISKTMP == "/system/bin") {
// Running with emulator.sh // Running with emulator.sh
if (SDK_INT >= 26) if (SDK_INT >= 26)
lsetfilecon("/system/bin/magisk", EXEC_CON); lsetfilecon("/system/bin/magisk", EXEC_CON);
return; return;
} }
if (MAGISKTMP == "/sbin") if (MAGISKTMP == "/sbin")
setfilecon(MAGISKTMP.data(), ROOT_CON); setfilecon(MAGISKTMP.data(), ROOT_CON);
else else
chmod(MAGISKTMP.data(), 0700); chmod(MAGISKTMP.data(), 0700);
auto dir = xopen_dir(MAGISKTMP.data()); auto dir = xopen_dir(MAGISKTMP.data());
int dfd = dirfd(dir.get()); int dfd = dirfd(dir.get());
for (dirent *entry; (entry = xreaddir(dir.get()));) { for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (SDK_INT >= 26 && entry->d_name == "magisk"sv) if (SDK_INT >= 26 && entry->d_name == "magisk"sv)
setfilecon_at(dfd, entry->d_name, EXEC_CON); setfilecon_at(dfd, entry->d_name, EXEC_CON);
else else
setfilecon_at(dfd, entry->d_name, SYSTEM_CON); setfilecon_at(dfd, entry->d_name, SYSTEM_CON);
} }
} }

View File

@@ -11,139 +11,139 @@ using namespace std;
#define BBEXEC_CMD bbpath(), "sh" #define BBEXEC_CMD bbpath(), "sh"
static const char *bbpath() { static const char *bbpath() {
static string path; static string path;
if (path.empty()) if (path.empty())
path = MAGISKTMP + "/" BBPATH "/busybox"; path = MAGISKTMP + "/" BBPATH "/busybox";
return path.data(); return path.data();
} }
static void set_script_env() { static void set_script_env() {
setenv("ASH_STANDALONE", "1", 1); setenv("ASH_STANDALONE", "1", 1);
char new_path[4096]; char new_path[4096];
sprintf(new_path, "%s:%s", getenv("PATH"), MAGISKTMP.data()); sprintf(new_path, "%s:%s", getenv("PATH"), MAGISKTMP.data());
setenv("PATH", new_path, 1); setenv("PATH", new_path, 1);
}; };
void exec_script(const char *script) { void exec_script(const char *script) {
exec_t exec { exec_t exec {
.pre_exec = set_script_env, .pre_exec = set_script_env,
.fork = fork_no_orphan .fork = fork_no_orphan
}; };
exec_command_sync(exec, BBEXEC_CMD, script); exec_command_sync(exec, BBEXEC_CMD, script);
} }
static timespec pfs_timeout; static timespec pfs_timeout;
#define PFS_SETUP() \ #define PFS_SETUP() \
if (pfs) { \ if (pfs) { \
if (int pid = xfork()) { \ if (int pid = xfork()) { \
if (pid < 0) \ if (pid < 0) \
return; \ return; \
/* In parent process, simply wait for child to finish */ \ /* In parent process, simply wait for child to finish */ \
waitpid(pid, nullptr, 0); \ waitpid(pid, nullptr, 0); \
return; \ return; \
} \ } \
timer_pid = xfork(); \ timer_pid = xfork(); \
if (timer_pid == 0) { \ if (timer_pid == 0) { \
/* In timer process, count down */ \ /* In timer process, count down */ \
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &pfs_timeout, nullptr); \ clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &pfs_timeout, nullptr); \
exit(0); \ exit(0); \
} \ } \
} }
#define PFS_WAIT() \ #define PFS_WAIT() \
if (pfs) { \ if (pfs) { \
/* If we ran out of time, don't block */ \ /* If we ran out of time, don't block */ \
if (timer_pid < 0) \ if (timer_pid < 0) \
continue; \ continue; \
if (int pid = waitpid(-1, nullptr, 0); pid == timer_pid) { \ if (int pid = waitpid(-1, nullptr, 0); pid == timer_pid) { \
LOGW("* post-fs-data scripts blocking phase timeout\n"); \ LOGW("* post-fs-data scripts blocking phase timeout\n"); \
timer_pid = -1; \ timer_pid = -1; \
} \ } \
} }
#define PFS_DONE() \ #define PFS_DONE() \
if (pfs) { \ if (pfs) { \
if (timer_pid > 0) \ if (timer_pid > 0) \
kill(timer_pid, SIGKILL); \ kill(timer_pid, SIGKILL); \
exit(0); \ exit(0); \
} }
void exec_common_scripts(const char *stage) { void exec_common_scripts(const char *stage) {
LOGI("* Running %s.d scripts\n", stage); LOGI("* Running %s.d scripts\n", stage);
char path[4096]; char path[4096];
char *name = path + sprintf(path, SECURE_DIR "/%s.d", stage); char *name = path + sprintf(path, SECURE_DIR "/%s.d", stage);
auto dir = xopen_dir(path); auto dir = xopen_dir(path);
if (!dir) return; if (!dir) return;
bool pfs = stage == "post-fs-data"sv; bool pfs = stage == "post-fs-data"sv;
int timer_pid = -1; int timer_pid = -1;
if (pfs) { if (pfs) {
// Setup timer // Setup timer
clock_gettime(CLOCK_MONOTONIC, &pfs_timeout); clock_gettime(CLOCK_MONOTONIC, &pfs_timeout);
pfs_timeout.tv_sec += POST_FS_DATA_SCRIPT_MAX_TIME; pfs_timeout.tv_sec += POST_FS_DATA_SCRIPT_MAX_TIME;
} }
PFS_SETUP() PFS_SETUP()
*(name++) = '/'; *(name++) = '/';
int dfd = dirfd(dir.get()); int dfd = dirfd(dir.get());
for (dirent *entry; (entry = xreaddir(dir.get()));) { for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_type == DT_REG) { if (entry->d_type == DT_REG) {
if (faccessat(dfd, entry->d_name, X_OK, 0) != 0) if (faccessat(dfd, entry->d_name, X_OK, 0) != 0)
continue; continue;
LOGI("%s.d: exec [%s]\n", stage, entry->d_name); LOGI("%s.d: exec [%s]\n", stage, entry->d_name);
strcpy(name, entry->d_name); strcpy(name, entry->d_name);
exec_t exec { exec_t exec {
.pre_exec = set_script_env, .pre_exec = set_script_env,
.fork = pfs ? xfork : fork_dont_care .fork = pfs ? xfork : fork_dont_care
}; };
exec_command(exec, BBEXEC_CMD, path); exec_command(exec, BBEXEC_CMD, path);
PFS_WAIT() PFS_WAIT()
} }
} }
PFS_DONE() PFS_DONE()
} }
// Return if a > b // Return if a > b
static bool timespec_larger(timespec *a, timespec *b) { static bool timespec_larger(timespec *a, timespec *b) {
if (a->tv_sec != b->tv_sec) if (a->tv_sec != b->tv_sec)
return a->tv_sec > b->tv_sec; return a->tv_sec > b->tv_sec;
return a->tv_nsec > b->tv_nsec; return a->tv_nsec > b->tv_nsec;
} }
void exec_module_scripts(const char *stage, const vector<string> &module_list) { void exec_module_scripts(const char *stage, const vector<string> &module_list) {
LOGI("* Running module %s scripts\n", stage); LOGI("* Running module %s scripts\n", stage);
if (module_list.empty()) if (module_list.empty())
return; return;
bool pfs = stage == "post-fs-data"sv; bool pfs = stage == "post-fs-data"sv;
if (pfs) { if (pfs) {
timespec now{}; timespec now{};
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
// If we had already timed out, treat it as service mode // If we had already timed out, treat it as service mode
if (timespec_larger(&now, &pfs_timeout)) if (timespec_larger(&now, &pfs_timeout))
pfs = false; pfs = false;
} }
int timer_pid = -1; int timer_pid = -1;
PFS_SETUP() PFS_SETUP()
char path[4096]; char path[4096];
for (auto &m : module_list) { for (auto &m : module_list) {
const char* module = m.data(); const char* module = m.data();
sprintf(path, MODULEROOT "/%s/%s.sh", module, stage); sprintf(path, MODULEROOT "/%s/%s.sh", module, stage);
if (access(path, F_OK) == -1) if (access(path, F_OK) == -1)
continue; continue;
LOGI("%s: exec [%s.sh]\n", module, stage); LOGI("%s: exec [%s.sh]\n", module, stage);
exec_t exec { exec_t exec {
.pre_exec = set_script_env, .pre_exec = set_script_env,
.fork = pfs ? xfork : fork_dont_care .fork = pfs ? xfork : fork_dont_care
}; };
exec_command(exec, BBEXEC_CMD, path); exec_command(exec, BBEXEC_CMD, path);
PFS_WAIT() PFS_WAIT()
} }
PFS_DONE() PFS_DONE()
} }
constexpr char install_script[] = R"EOF( constexpr char install_script[] = R"EOF(
@@ -154,23 +154,23 @@ rm -f $APK
)EOF"; )EOF";
void install_apk(const char *apk) { void install_apk(const char *apk) {
setfilecon(apk, "u:object_r:" SEPOL_FILE_TYPE ":s0"); setfilecon(apk, "u:object_r:" SEPOL_FILE_TYPE ":s0");
exec_t exec { exec_t exec {
.fork = fork_no_orphan .fork = fork_no_orphan
}; };
char cmds[sizeof(install_script) + 4096]; char cmds[sizeof(install_script) + 4096];
sprintf(cmds, install_script, apk); sprintf(cmds, install_script, apk);
exec_command_sync(exec, "/system/bin/sh", "-c", cmds); exec_command_sync(exec, "/system/bin/sh", "-c", cmds);
} }
[[noreturn]] __printflike(2, 3) [[noreturn]] __printflike(2, 3)
static void abort(FILE *fp, const char *fmt, ...) { static void abort(FILE *fp, const char *fmt, ...) {
va_list valist; va_list valist;
va_start(valist, fmt); va_start(valist, fmt);
vfprintf(fp, fmt, valist); vfprintf(fp, fmt, valist);
fprintf(fp, "\n\n"); fprintf(fp, "\n\n");
va_end(valist); va_end(valist);
exit(1); exit(1);
} }
constexpr char install_module_script[] = R"EOF( constexpr char install_module_script[] = R"EOF(
@@ -181,24 +181,24 @@ exit 0'
)EOF"; )EOF";
void install_module(const char *file) { void install_module(const char *file) {
if (getuid() != 0) if (getuid() != 0)
abort(stderr, "Run this command with root"); abort(stderr, "Run this command with root");
if (access(DATABIN, F_OK) || if (access(DATABIN, F_OK) ||
access(DATABIN "/busybox", X_OK) || access(DATABIN "/busybox", X_OK) ||
access(DATABIN "/util_functions.sh", F_OK)) access(DATABIN "/util_functions.sh", F_OK))
abort(stderr, "Incomplete Magisk install"); abort(stderr, "Incomplete Magisk install");
if (access(file, F_OK)) if (access(file, F_OK))
abort(stderr, "'%s' does not exist", file); abort(stderr, "'%s' does not exist", file);
setenv("OUTFD", "1", 1); setenv("OUTFD", "1", 1);
setenv("ZIPFILE", file, 1); setenv("ZIPFILE", file, 1);
setenv("ASH_STANDALONE", "1", 1); setenv("ASH_STANDALONE", "1", 1);
int fd = xopen("/dev/null", O_RDONLY); int fd = xopen("/dev/null", O_RDONLY);
xdup2(fd, STDERR_FILENO); xdup2(fd, STDERR_FILENO);
close(fd); close(fd);
const char *argv[] = { "/system/bin/sh", "-c", install_module_script, nullptr }; const char *argv[] = { "/system/bin/sh", "-c", install_module_script, nullptr };
execve(argv[0], (char **) argv, environ); execve(argv[0], (char **) argv, environ);
abort(stdout, "Failed to execute BusyBox shell"); abort(stdout, "Failed to execute BusyBox shell");
} }

View File

@@ -7,30 +7,30 @@
#include <utils.hpp> #include <utils.hpp>
static size_t socket_len(sockaddr_un *sun) { static size_t socket_len(sockaddr_un *sun) {
if (sun->sun_path[0]) if (sun->sun_path[0])
return sizeof(sa_family_t) + strlen(sun->sun_path) + 1; return sizeof(sa_family_t) + strlen(sun->sun_path) + 1;
else else
return sizeof(sa_family_t) + strlen(sun->sun_path + 1) + 1; return sizeof(sa_family_t) + strlen(sun->sun_path + 1) + 1;
} }
socklen_t setup_sockaddr(sockaddr_un *sun, const char *name) { socklen_t setup_sockaddr(sockaddr_un *sun, const char *name) {
memset(sun, 0, sizeof(*sun)); memset(sun, 0, sizeof(*sun));
sun->sun_family = AF_UNIX; sun->sun_family = AF_UNIX;
strcpy(sun->sun_path + 1, name); strcpy(sun->sun_path + 1, name);
return socket_len(sun); return socket_len(sun);
} }
int socket_accept(int sockfd, int timeout) { int socket_accept(int sockfd, int timeout) {
struct pollfd pfd = { struct pollfd pfd = {
.fd = sockfd, .fd = sockfd,
.events = POLL_IN .events = POLL_IN
}; };
return xpoll(&pfd, 1, timeout * 1000) <= 0 ? -1 : xaccept4(sockfd, nullptr, nullptr, SOCK_CLOEXEC); return xpoll(&pfd, 1, timeout * 1000) <= 0 ? -1 : xaccept4(sockfd, nullptr, nullptr, SOCK_CLOEXEC);
} }
void get_client_cred(int fd, struct ucred *cred) { void get_client_cred(int fd, struct ucred *cred) {
socklen_t ucred_length = sizeof(*cred); socklen_t ucred_length = sizeof(*cred);
getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &ucred_length); getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &ucred_length);
} }
/* /*
@@ -43,50 +43,50 @@ void get_client_cred(int fd, struct ucred *cred) {
* On error the function terminates by calling exit(-1) * On error the function terminates by calling exit(-1)
*/ */
int recv_fd(int sockfd) { int recv_fd(int sockfd) {
// Need to receive data from the message, otherwise don't care about it. // Need to receive data from the message, otherwise don't care about it.
char iovbuf; char iovbuf;
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
struct iovec iov = { struct iovec iov = {
.iov_base = &iovbuf, .iov_base = &iovbuf,
.iov_len = 1, .iov_len = 1,
}; };
char cmsgbuf[CMSG_SPACE(sizeof(int))]; char cmsgbuf[CMSG_SPACE(sizeof(int))];
struct msghdr msg = { struct msghdr msg = {
.msg_iov = &iov, .msg_iov = &iov,
.msg_iovlen = 1, .msg_iovlen = 1,
.msg_control = cmsgbuf, .msg_control = cmsgbuf,
.msg_controllen = sizeof(cmsgbuf), .msg_controllen = sizeof(cmsgbuf),
}; };
xrecvmsg(sockfd, &msg, MSG_WAITALL); xrecvmsg(sockfd, &msg, MSG_WAITALL);
// Was a control message actually sent? // Was a control message actually sent?
switch (msg.msg_controllen) { switch (msg.msg_controllen) {
case 0: case 0:
// No, so the file descriptor was closed and won't be used. // No, so the file descriptor was closed and won't be used.
return -1; return -1;
case sizeof(cmsgbuf): case sizeof(cmsgbuf):
// Yes, grab the file descriptor from it. // Yes, grab the file descriptor from it.
break; break;
default: default:
goto error; goto error;
} }
cmsg = CMSG_FIRSTHDR(&msg); cmsg = CMSG_FIRSTHDR(&msg);
if (cmsg == nullptr || if (cmsg == nullptr ||
cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_level != SOL_SOCKET ||
cmsg->cmsg_type != SCM_RIGHTS) { cmsg->cmsg_type != SCM_RIGHTS) {
error: error:
LOGE("unable to read fd\n"); LOGE("unable to read fd\n");
exit(-1); exit(-1);
} }
return *(int *)CMSG_DATA(cmsg); return *(int *)CMSG_DATA(cmsg);
} }
/* /*
@@ -99,106 +99,106 @@ error:
* but no control message with the FD is sent. * but no control message with the FD is sent.
*/ */
void send_fd(int sockfd, int fd) { void send_fd(int sockfd, int fd) {
// Need to send some data in the message, this will do. // Need to send some data in the message, this will do.
char junk[] = { '\0' }; char junk[] = { '\0' };
struct iovec iov = { struct iovec iov = {
.iov_base = junk, .iov_base = junk,
.iov_len = 1, .iov_len = 1,
}; };
struct msghdr msg = { struct msghdr msg = {
.msg_iov = &iov, .msg_iov = &iov,
.msg_iovlen = 1, .msg_iovlen = 1,
}; };
char cmsgbuf[CMSG_SPACE(sizeof(int))]; char cmsgbuf[CMSG_SPACE(sizeof(int))];
if (fd != -1) { if (fd != -1) {
// Is the file descriptor actually open? // Is the file descriptor actually open?
if (fcntl(fd, F_GETFD) == -1) { if (fcntl(fd, F_GETFD) == -1) {
if (errno != EBADF) { if (errno != EBADF) {
PLOGE("unable to send fd"); PLOGE("unable to send fd");
} }
// It's closed, don't send a control message or sendmsg will EBADF. // It's closed, don't send a control message or sendmsg will EBADF.
} else { } else {
// It's open, send the file descriptor in a control message. // It's open, send the file descriptor in a control message.
msg.msg_control = cmsgbuf; msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf); msg.msg_controllen = sizeof(cmsgbuf);
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_type = SCM_RIGHTS;
*(int *)CMSG_DATA(cmsg) = fd; *(int *)CMSG_DATA(cmsg) = fd;
} }
} }
xsendmsg(sockfd, &msg, 0); xsendmsg(sockfd, &msg, 0);
} }
int read_int(int fd) { int read_int(int fd) {
int val; int val;
if (xxread(fd, &val, sizeof(val)) != sizeof(val)) if (xxread(fd, &val, sizeof(val)) != sizeof(val))
return -1; return -1;
return val; return val;
} }
int read_int_be(int fd) { int read_int_be(int fd) {
uint32_t val; uint32_t val;
if (xxread(fd, &val, sizeof(val)) != sizeof(val)) if (xxread(fd, &val, sizeof(val)) != sizeof(val))
return -1; return -1;
return ntohl(val); return ntohl(val);
} }
void write_int(int fd, int val) { void write_int(int fd, int val) {
if (fd < 0) return; if (fd < 0) return;
xwrite(fd, &val, sizeof(val)); xwrite(fd, &val, sizeof(val));
} }
void write_int_be(int fd, int val) { void write_int_be(int fd, int val) {
uint32_t nl = htonl(val); uint32_t nl = htonl(val);
xwrite(fd, &nl, sizeof(nl)); xwrite(fd, &nl, sizeof(nl));
} }
static char *rd_str(int fd, int len) { static char *rd_str(int fd, int len) {
char *val = (char *) xmalloc(sizeof(char) * (len + 1)); char *val = (char *) xmalloc(sizeof(char) * (len + 1));
xxread(fd, val, len); xxread(fd, val, len);
val[len] = '\0'; val[len] = '\0';
return val; return val;
} }
char* read_string(int fd) { char* read_string(int fd) {
int len = read_int(fd); int len = read_int(fd);
return rd_str(fd, len); return rd_str(fd, len);
} }
char* read_string_be(int fd) { char* read_string_be(int fd) {
int len = read_int_be(fd); int len = read_int_be(fd);
return rd_str(fd, len); return rd_str(fd, len);
} }
void write_string(int fd, const char *val) { void write_string(int fd, const char *val) {
if (fd < 0) return; if (fd < 0) return;
int len = strlen(val); int len = strlen(val);
write_int(fd, len); write_int(fd, len);
xwrite(fd, val, len); xwrite(fd, val, len);
} }
void write_string_be(int fd, const char *val) { void write_string_be(int fd, const char *val) {
int len = strlen(val); int len = strlen(val);
write_int_be(fd, len); write_int_be(fd, len);
xwrite(fd, val, len); xwrite(fd, val, len);
} }
void write_key_value(int fd, const char *key, const char *val) { void write_key_value(int fd, const char *key, const char *val) {
write_string_be(fd, key); write_string_be(fd, key);
write_string_be(fd, val); write_string_be(fd, val);
} }
void write_key_token(int fd, const char *key, int tok) { void write_key_token(int fd, const char *key, int tok) {
char val[16]; char val[16];
sprintf(val, "%d", tok); sprintf(val, "%d", tok);
write_key_value(fd, key, val); write_key_value(fd, key, val);
} }

View File

@@ -6,9 +6,9 @@ LOCAL_MODULE:= libxz
LOCAL_C_INCLUDES := $(LOCAL_PATH)/xz-embedded LOCAL_C_INCLUDES := $(LOCAL_PATH)/xz-embedded
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
xz-embedded/xz_crc32.c \ xz-embedded/xz_crc32.c \
xz-embedded/xz_dec_lzma2.c \ xz-embedded/xz_dec_lzma2.c \
xz-embedded/xz_dec_stream.c xz-embedded/xz_dec_stream.c
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
# libnanopb.a # libnanopb.a
@@ -17,9 +17,9 @@ LOCAL_MODULE:= libnanopb
LOCAL_C_INCLUDES := $(LOCAL_PATH)/nanopb LOCAL_C_INCLUDES := $(LOCAL_PATH)/nanopb
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
nanopb/pb_common.c \ nanopb/pb_common.c \
nanopb/pb_decode.c \ nanopb/pb_decode.c \
nanopb/pb_encode.c nanopb/pb_encode.c
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
# libfdt.a # libfdt.a
@@ -28,15 +28,15 @@ LOCAL_MODULE:= libfdt
LOCAL_C_INCLUDES := $(LOCAL_PATH)/dtc/libfdt LOCAL_C_INCLUDES := $(LOCAL_PATH)/dtc/libfdt
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
dtc/libfdt/fdt.c \ dtc/libfdt/fdt.c \
dtc/libfdt/fdt_addresses.c \ dtc/libfdt/fdt_addresses.c \
dtc/libfdt/fdt_empty_tree.c \ dtc/libfdt/fdt_empty_tree.c \
dtc/libfdt/fdt_overlay.c \ dtc/libfdt/fdt_overlay.c \
dtc/libfdt/fdt_ro.c \ dtc/libfdt/fdt_ro.c \
dtc/libfdt/fdt_rw.c \ dtc/libfdt/fdt_rw.c \
dtc/libfdt/fdt_strerror.c \ dtc/libfdt/fdt_strerror.c \
dtc/libfdt/fdt_sw.c \ dtc/libfdt/fdt_sw.c \
dtc/libfdt/fdt_wip.c dtc/libfdt/fdt_wip.c
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
# liblz4.a # liblz4.a
@@ -45,10 +45,10 @@ LOCAL_MODULE := liblz4
LOCAL_C_INCLUDES := $(LOCAL_PATH)/lz4/lib LOCAL_C_INCLUDES := $(LOCAL_PATH)/lz4/lib
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
lz4/lib/lz4.c \ lz4/lib/lz4.c \
lz4/lib/lz4frame.c \ lz4/lib/lz4frame.c \
lz4/lib/lz4hc.c \ lz4/lib/lz4hc.c \
lz4/lib/xxhash.c lz4/lib/xxhash.c
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
# libbz2.a # libbz2.a
@@ -57,112 +57,112 @@ LOCAL_MODULE := libbz2
LOCAL_C_INCLUDES := $(LOCAL_PATH)/bzip2 LOCAL_C_INCLUDES := $(LOCAL_PATH)/bzip2
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
bzip2/blocksort.c \ bzip2/blocksort.c \
bzip2/huffman.c \ bzip2/huffman.c \
bzip2/crctable.c \ bzip2/crctable.c \
bzip2/randtable.c \ bzip2/randtable.c \
bzip2/compress.c \ bzip2/compress.c \
bzip2/decompress.c \ bzip2/decompress.c \
bzip2/bzlib.c bzip2/bzlib.c
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
# liblzma.a # liblzma.a
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := liblzma LOCAL_MODULE := liblzma
LOCAL_C_INCLUDES := \ LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/xz_config \ $(LOCAL_PATH)/xz_config \
$(LOCAL_PATH)/xz/src/common \ $(LOCAL_PATH)/xz/src/common \
$(LOCAL_PATH)/xz/src/liblzma/api \ $(LOCAL_PATH)/xz/src/liblzma/api \
$(LOCAL_PATH)/xz/src/liblzma/check \ $(LOCAL_PATH)/xz/src/liblzma/check \
$(LOCAL_PATH)/xz/src/liblzma/common \ $(LOCAL_PATH)/xz/src/liblzma/common \
$(LOCAL_PATH)/xz/src/liblzma/delta \ $(LOCAL_PATH)/xz/src/liblzma/delta \
$(LOCAL_PATH)/xz/src/liblzma/lz \ $(LOCAL_PATH)/xz/src/liblzma/lz \
$(LOCAL_PATH)/xz/src/liblzma/lzma \ $(LOCAL_PATH)/xz/src/liblzma/lzma \
$(LOCAL_PATH)/xz/src/liblzma/rangecoder \ $(LOCAL_PATH)/xz/src/liblzma/rangecoder \
$(LOCAL_PATH)/xz/src/liblzma/simple \ $(LOCAL_PATH)/xz/src/liblzma/simple \
$(LOCAL_PATH)/xz/src/liblzma $(LOCAL_PATH)/xz/src/liblzma
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/xz/src/liblzma/api LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/xz/src/liblzma/api
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
xz/src/common/tuklib_cpucores.c \ xz/src/common/tuklib_cpucores.c \
xz/src/common/tuklib_exit.c \ xz/src/common/tuklib_exit.c \
xz/src/common/tuklib_mbstr_fw.c \ xz/src/common/tuklib_mbstr_fw.c \
xz/src/common/tuklib_mbstr_width.c \ xz/src/common/tuklib_mbstr_width.c \
xz/src/common/tuklib_open_stdxxx.c \ xz/src/common/tuklib_open_stdxxx.c \
xz/src/common/tuklib_physmem.c \ xz/src/common/tuklib_physmem.c \
xz/src/common/tuklib_progname.c \ xz/src/common/tuklib_progname.c \
xz/src/liblzma/check/check.c \ xz/src/liblzma/check/check.c \
xz/src/liblzma/check/crc32_fast.c \ xz/src/liblzma/check/crc32_fast.c \
xz/src/liblzma/check/crc32_table.c \ xz/src/liblzma/check/crc32_table.c \
xz/src/liblzma/check/crc64_fast.c \ xz/src/liblzma/check/crc64_fast.c \
xz/src/liblzma/check/crc64_table.c \ xz/src/liblzma/check/crc64_table.c \
xz/src/liblzma/check/sha256.c \ xz/src/liblzma/check/sha256.c \
xz/src/liblzma/common/alone_decoder.c \ xz/src/liblzma/common/alone_decoder.c \
xz/src/liblzma/common/alone_encoder.c \ xz/src/liblzma/common/alone_encoder.c \
xz/src/liblzma/common/auto_decoder.c \ xz/src/liblzma/common/auto_decoder.c \
xz/src/liblzma/common/block_buffer_decoder.c \ xz/src/liblzma/common/block_buffer_decoder.c \
xz/src/liblzma/common/block_buffer_encoder.c \ xz/src/liblzma/common/block_buffer_encoder.c \
xz/src/liblzma/common/block_decoder.c \ xz/src/liblzma/common/block_decoder.c \
xz/src/liblzma/common/block_encoder.c \ xz/src/liblzma/common/block_encoder.c \
xz/src/liblzma/common/block_header_decoder.c \ xz/src/liblzma/common/block_header_decoder.c \
xz/src/liblzma/common/block_header_encoder.c \ xz/src/liblzma/common/block_header_encoder.c \
xz/src/liblzma/common/block_util.c \ xz/src/liblzma/common/block_util.c \
xz/src/liblzma/common/common.c \ xz/src/liblzma/common/common.c \
xz/src/liblzma/common/easy_buffer_encoder.c \ xz/src/liblzma/common/easy_buffer_encoder.c \
xz/src/liblzma/common/easy_decoder_memusage.c \ xz/src/liblzma/common/easy_decoder_memusage.c \
xz/src/liblzma/common/easy_encoder.c \ xz/src/liblzma/common/easy_encoder.c \
xz/src/liblzma/common/easy_encoder_memusage.c \ xz/src/liblzma/common/easy_encoder_memusage.c \
xz/src/liblzma/common/easy_preset.c \ xz/src/liblzma/common/easy_preset.c \
xz/src/liblzma/common/filter_buffer_decoder.c \ xz/src/liblzma/common/filter_buffer_decoder.c \
xz/src/liblzma/common/filter_buffer_encoder.c \ xz/src/liblzma/common/filter_buffer_encoder.c \
xz/src/liblzma/common/filter_common.c \ xz/src/liblzma/common/filter_common.c \
xz/src/liblzma/common/filter_decoder.c \ xz/src/liblzma/common/filter_decoder.c \
xz/src/liblzma/common/filter_encoder.c \ xz/src/liblzma/common/filter_encoder.c \
xz/src/liblzma/common/filter_flags_decoder.c \ xz/src/liblzma/common/filter_flags_decoder.c \
xz/src/liblzma/common/filter_flags_encoder.c \ xz/src/liblzma/common/filter_flags_encoder.c \
xz/src/liblzma/common/hardware_cputhreads.c \ xz/src/liblzma/common/hardware_cputhreads.c \
xz/src/liblzma/common/hardware_physmem.c \ xz/src/liblzma/common/hardware_physmem.c \
xz/src/liblzma/common/index.c \ xz/src/liblzma/common/index.c \
xz/src/liblzma/common/index_decoder.c \ xz/src/liblzma/common/index_decoder.c \
xz/src/liblzma/common/index_encoder.c \ xz/src/liblzma/common/index_encoder.c \
xz/src/liblzma/common/index_hash.c \ xz/src/liblzma/common/index_hash.c \
xz/src/liblzma/common/outqueue.c \ xz/src/liblzma/common/outqueue.c \
xz/src/liblzma/common/stream_buffer_decoder.c \ xz/src/liblzma/common/stream_buffer_decoder.c \
xz/src/liblzma/common/stream_buffer_encoder.c \ xz/src/liblzma/common/stream_buffer_encoder.c \
xz/src/liblzma/common/stream_decoder.c \ xz/src/liblzma/common/stream_decoder.c \
xz/src/liblzma/common/stream_encoder.c \ xz/src/liblzma/common/stream_encoder.c \
xz/src/liblzma/common/stream_encoder_mt.c \ xz/src/liblzma/common/stream_encoder_mt.c \
xz/src/liblzma/common/stream_flags_common.c \ xz/src/liblzma/common/stream_flags_common.c \
xz/src/liblzma/common/stream_flags_decoder.c \ xz/src/liblzma/common/stream_flags_decoder.c \
xz/src/liblzma/common/stream_flags_encoder.c \ xz/src/liblzma/common/stream_flags_encoder.c \
xz/src/liblzma/common/vli_decoder.c \ xz/src/liblzma/common/vli_decoder.c \
xz/src/liblzma/common/vli_encoder.c \ xz/src/liblzma/common/vli_encoder.c \
xz/src/liblzma/common/vli_size.c \ xz/src/liblzma/common/vli_size.c \
xz/src/liblzma/delta/delta_common.c \ xz/src/liblzma/delta/delta_common.c \
xz/src/liblzma/delta/delta_decoder.c \ xz/src/liblzma/delta/delta_decoder.c \
xz/src/liblzma/delta/delta_encoder.c \ xz/src/liblzma/delta/delta_encoder.c \
xz/src/liblzma/lz/lz_decoder.c \ xz/src/liblzma/lz/lz_decoder.c \
xz/src/liblzma/lz/lz_encoder.c \ xz/src/liblzma/lz/lz_encoder.c \
xz/src/liblzma/lz/lz_encoder_mf.c \ xz/src/liblzma/lz/lz_encoder_mf.c \
xz/src/liblzma/lzma/fastpos_table.c \ xz/src/liblzma/lzma/fastpos_table.c \
xz/src/liblzma/lzma/fastpos_tablegen.c \ xz/src/liblzma/lzma/fastpos_tablegen.c \
xz/src/liblzma/lzma/lzma2_decoder.c \ xz/src/liblzma/lzma/lzma2_decoder.c \
xz/src/liblzma/lzma/lzma2_encoder.c \ xz/src/liblzma/lzma/lzma2_encoder.c \
xz/src/liblzma/lzma/lzma_decoder.c \ xz/src/liblzma/lzma/lzma_decoder.c \
xz/src/liblzma/lzma/lzma_encoder.c \ xz/src/liblzma/lzma/lzma_encoder.c \
xz/src/liblzma/lzma/lzma_encoder_optimum_fast.c \ xz/src/liblzma/lzma/lzma_encoder_optimum_fast.c \
xz/src/liblzma/lzma/lzma_encoder_optimum_normal.c \ xz/src/liblzma/lzma/lzma_encoder_optimum_normal.c \
xz/src/liblzma/lzma/lzma_encoder_presets.c \ xz/src/liblzma/lzma/lzma_encoder_presets.c \
xz/src/liblzma/rangecoder/price_table.c \ xz/src/liblzma/rangecoder/price_table.c \
xz/src/liblzma/rangecoder/price_tablegen.c \ xz/src/liblzma/rangecoder/price_tablegen.c \
xz/src/liblzma/simple/arm.c \ xz/src/liblzma/simple/arm.c \
xz/src/liblzma/simple/armthumb.c \ xz/src/liblzma/simple/armthumb.c \
xz/src/liblzma/simple/ia64.c \ xz/src/liblzma/simple/ia64.c \
xz/src/liblzma/simple/powerpc.c \ xz/src/liblzma/simple/powerpc.c \
xz/src/liblzma/simple/simple_coder.c \ xz/src/liblzma/simple/simple_coder.c \
xz/src/liblzma/simple/simple_decoder.c \ xz/src/liblzma/simple/simple_decoder.c \
xz/src/liblzma/simple/simple_encoder.c \ xz/src/liblzma/simple/simple_encoder.c \
xz/src/liblzma/simple/sparc.c \ xz/src/liblzma/simple/sparc.c \
xz/src/liblzma/simple/x86.c xz/src/liblzma/simple/x86.c
LOCAL_CFLAGS := -DHAVE_CONFIG_H -Wno-implicit-function-declaration LOCAL_CFLAGS := -DHAVE_CONFIG_H -Wno-implicit-function-declaration
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
@@ -175,72 +175,72 @@ LOCAL_MODULE := libsepol
LOCAL_C_INCLUDES := $(LIBSEPOL) $(LOCAL_PATH)/selinux/libsepol/src LOCAL_C_INCLUDES := $(LIBSEPOL) $(LOCAL_PATH)/selinux/libsepol/src
LOCAL_EXPORT_C_INCLUDES := $(LIBSEPOL) LOCAL_EXPORT_C_INCLUDES := $(LIBSEPOL)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
selinux/libsepol/src/assertion.c \ selinux/libsepol/src/assertion.c \
selinux/libsepol/src/avrule_block.c \ selinux/libsepol/src/avrule_block.c \
selinux/libsepol/src/avtab.c \ selinux/libsepol/src/avtab.c \
selinux/libsepol/src/boolean_record.c \ selinux/libsepol/src/boolean_record.c \
selinux/libsepol/src/booleans.c \ selinux/libsepol/src/booleans.c \
selinux/libsepol/src/conditional.c \ selinux/libsepol/src/conditional.c \
selinux/libsepol/src/constraint.c \ selinux/libsepol/src/constraint.c \
selinux/libsepol/src/context.c \ selinux/libsepol/src/context.c \
selinux/libsepol/src/context_record.c \ selinux/libsepol/src/context_record.c \
selinux/libsepol/src/debug.c \ selinux/libsepol/src/debug.c \
selinux/libsepol/src/deprecated_funcs.c \ selinux/libsepol/src/deprecated_funcs.c \
selinux/libsepol/src/ebitmap.c \ selinux/libsepol/src/ebitmap.c \
selinux/libsepol/src/expand.c \ selinux/libsepol/src/expand.c \
selinux/libsepol/src/handle.c \ selinux/libsepol/src/handle.c \
selinux/libsepol/src/hashtab.c \ selinux/libsepol/src/hashtab.c \
selinux/libsepol/src/hierarchy.c \ selinux/libsepol/src/hierarchy.c \
selinux/libsepol/src/ibendport_record.c \ selinux/libsepol/src/ibendport_record.c \
selinux/libsepol/src/ibendports.c \ selinux/libsepol/src/ibendports.c \
selinux/libsepol/src/ibpkey_record.c \ selinux/libsepol/src/ibpkey_record.c \
selinux/libsepol/src/ibpkeys.c \ selinux/libsepol/src/ibpkeys.c \
selinux/libsepol/src/iface_record.c \ selinux/libsepol/src/iface_record.c \
selinux/libsepol/src/interfaces.c \ selinux/libsepol/src/interfaces.c \
selinux/libsepol/src/kernel_to_cil.c \ selinux/libsepol/src/kernel_to_cil.c \
selinux/libsepol/src/kernel_to_common.c \ selinux/libsepol/src/kernel_to_common.c \
selinux/libsepol/src/kernel_to_conf.c \ selinux/libsepol/src/kernel_to_conf.c \
selinux/libsepol/src/link.c \ selinux/libsepol/src/link.c \
selinux/libsepol/src/mls.c \ selinux/libsepol/src/mls.c \
selinux/libsepol/src/module.c \ selinux/libsepol/src/module.c \
selinux/libsepol/src/module_to_cil.c \ selinux/libsepol/src/module_to_cil.c \
selinux/libsepol/src/node_record.c \ selinux/libsepol/src/node_record.c \
selinux/libsepol/src/nodes.c \ selinux/libsepol/src/nodes.c \
selinux/libsepol/src/optimize.c \ selinux/libsepol/src/optimize.c \
selinux/libsepol/src/polcaps.c \ selinux/libsepol/src/polcaps.c \
selinux/libsepol/src/policydb.c \ selinux/libsepol/src/policydb.c \
selinux/libsepol/src/policydb_convert.c \ selinux/libsepol/src/policydb_convert.c \
selinux/libsepol/src/policydb_public.c \ selinux/libsepol/src/policydb_public.c \
selinux/libsepol/src/port_record.c \ selinux/libsepol/src/port_record.c \
selinux/libsepol/src/ports.c \ selinux/libsepol/src/ports.c \
selinux/libsepol/src/roles.c \ selinux/libsepol/src/roles.c \
selinux/libsepol/src/services.c \ selinux/libsepol/src/services.c \
selinux/libsepol/src/sidtab.c \ selinux/libsepol/src/sidtab.c \
selinux/libsepol/src/symtab.c \ selinux/libsepol/src/symtab.c \
selinux/libsepol/src/user_record.c \ selinux/libsepol/src/user_record.c \
selinux/libsepol/src/users.c \ selinux/libsepol/src/users.c \
selinux/libsepol/src/util.c \ selinux/libsepol/src/util.c \
selinux/libsepol/src/write.c \ selinux/libsepol/src/write.c \
selinux/libsepol/cil/src/cil.c \ selinux/libsepol/cil/src/cil.c \
selinux/libsepol/cil/src/cil_binary.c \ selinux/libsepol/cil/src/cil_binary.c \
selinux/libsepol/cil/src/cil_build_ast.c \ selinux/libsepol/cil/src/cil_build_ast.c \
selinux/libsepol/cil/src/cil_copy_ast.c \ selinux/libsepol/cil/src/cil_copy_ast.c \
selinux/libsepol/cil/src/cil_find.c \ selinux/libsepol/cil/src/cil_find.c \
selinux/libsepol/cil/src/cil_fqn.c \ selinux/libsepol/cil/src/cil_fqn.c \
selinux/libsepol/cil/src/cil_lexer.c \ selinux/libsepol/cil/src/cil_lexer.c \
selinux/libsepol/cil/src/cil_list.c \ selinux/libsepol/cil/src/cil_list.c \
selinux/libsepol/cil/src/cil_log.c \ selinux/libsepol/cil/src/cil_log.c \
selinux/libsepol/cil/src/cil_mem.c \ selinux/libsepol/cil/src/cil_mem.c \
selinux/libsepol/cil/src/cil_parser.c \ selinux/libsepol/cil/src/cil_parser.c \
selinux/libsepol/cil/src/cil_policy.c \ selinux/libsepol/cil/src/cil_policy.c \
selinux/libsepol/cil/src/cil_post.c \ selinux/libsepol/cil/src/cil_post.c \
selinux/libsepol/cil/src/cil_reset_ast.c \ selinux/libsepol/cil/src/cil_reset_ast.c \
selinux/libsepol/cil/src/cil_resolve_ast.c \ selinux/libsepol/cil/src/cil_resolve_ast.c \
selinux/libsepol/cil/src/cil_stack.c \ selinux/libsepol/cil/src/cil_stack.c \
selinux/libsepol/cil/src/cil_strpool.c \ selinux/libsepol/cil/src/cil_strpool.c \
selinux/libsepol/cil/src/cil_symtab.c \ selinux/libsepol/cil/src/cil_symtab.c \
selinux/libsepol/cil/src/cil_tree.c \ selinux/libsepol/cil/src/cil_tree.c \
selinux/libsepol/cil/src/cil_verify.c selinux/libsepol/cil/src/cil_verify.c
LOCAL_CFLAGS := -Dgetline=__getline -Wno-implicit-function-declaration LOCAL_CFLAGS := -Dgetline=__getline -Wno-implicit-function-declaration
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
@@ -252,67 +252,67 @@ LOCAL_C_INCLUDES := $(LIBSELINUX)
LOCAL_EXPORT_C_INCLUDES := $(LIBSELINUX) LOCAL_EXPORT_C_INCLUDES := $(LIBSELINUX)
LOCAL_STATIC_LIBRARIES := libpcre2 LOCAL_STATIC_LIBRARIES := libpcre2
LOCAL_CFLAGS := \ LOCAL_CFLAGS := \
-Wno-implicit-function-declaration -Wno-int-conversion -Wno-unused-function \ -Wno-implicit-function-declaration -Wno-int-conversion -Wno-unused-function \
-D_GNU_SOURCE -DUSE_PCRE2 \ -D_GNU_SOURCE -DUSE_PCRE2 \
-DNO_PERSISTENTLY_STORED_PATTERNS -DDISABLE_SETRANS -DDISABLE_BOOL \ -DNO_PERSISTENTLY_STORED_PATTERNS -DDISABLE_SETRANS -DDISABLE_BOOL \
-DNO_MEDIA_BACKEND -DNO_X_BACKEND -DNO_DB_BACKEND -DNO_ANDROID_BACKEND -DNO_MEDIA_BACKEND -DNO_X_BACKEND -DNO_DB_BACKEND -DNO_ANDROID_BACKEND
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
selinux/libselinux/src/avc.c \ selinux/libselinux/src/avc.c \
selinux/libselinux/src/avc_internal.c \ selinux/libselinux/src/avc_internal.c \
selinux/libselinux/src/avc_sidtab.c \ selinux/libselinux/src/avc_sidtab.c \
selinux/libselinux/src/booleans.c \ selinux/libselinux/src/booleans.c \
selinux/libselinux/src/callbacks.c \ selinux/libselinux/src/callbacks.c \
selinux/libselinux/src/canonicalize_context.c \ selinux/libselinux/src/canonicalize_context.c \
selinux/libselinux/src/checkAccess.c \ selinux/libselinux/src/checkAccess.c \
selinux/libselinux/src/check_context.c \ selinux/libselinux/src/check_context.c \
selinux/libselinux/src/checkreqprot.c \ selinux/libselinux/src/checkreqprot.c \
selinux/libselinux/src/compute_av.c \ selinux/libselinux/src/compute_av.c \
selinux/libselinux/src/compute_create.c \ selinux/libselinux/src/compute_create.c \
selinux/libselinux/src/compute_member.c \ selinux/libselinux/src/compute_member.c \
selinux/libselinux/src/compute_relabel.c \ selinux/libselinux/src/compute_relabel.c \
selinux/libselinux/src/compute_user.c \ selinux/libselinux/src/compute_user.c \
selinux/libselinux/src/context.c \ selinux/libselinux/src/context.c \
selinux/libselinux/src/deny_unknown.c \ selinux/libselinux/src/deny_unknown.c \
selinux/libselinux/src/disable.c \ selinux/libselinux/src/disable.c \
selinux/libselinux/src/enabled.c \ selinux/libselinux/src/enabled.c \
selinux/libselinux/src/fgetfilecon.c \ selinux/libselinux/src/fgetfilecon.c \
selinux/libselinux/src/freecon.c \ selinux/libselinux/src/freecon.c \
selinux/libselinux/src/freeconary.c \ selinux/libselinux/src/freeconary.c \
selinux/libselinux/src/fsetfilecon.c \ selinux/libselinux/src/fsetfilecon.c \
selinux/libselinux/src/get_context_list.c \ selinux/libselinux/src/get_context_list.c \
selinux/libselinux/src/get_default_type.c \ selinux/libselinux/src/get_default_type.c \
selinux/libselinux/src/get_initial_context.c \ selinux/libselinux/src/get_initial_context.c \
selinux/libselinux/src/getenforce.c \ selinux/libselinux/src/getenforce.c \
selinux/libselinux/src/getfilecon.c \ selinux/libselinux/src/getfilecon.c \
selinux/libselinux/src/getpeercon.c \ selinux/libselinux/src/getpeercon.c \
selinux/libselinux/src/init.c \ selinux/libselinux/src/init.c \
selinux/libselinux/src/is_customizable_type.c \ selinux/libselinux/src/is_customizable_type.c \
selinux/libselinux/src/label.c \ selinux/libselinux/src/label.c \
selinux/libselinux/src/label_file.c \ selinux/libselinux/src/label_file.c \
selinux/libselinux/src/label_support.c \ selinux/libselinux/src/label_support.c \
selinux/libselinux/src/lgetfilecon.c \ selinux/libselinux/src/lgetfilecon.c \
selinux/libselinux/src/load_policy.c \ selinux/libselinux/src/load_policy.c \
selinux/libselinux/src/lsetfilecon.c \ selinux/libselinux/src/lsetfilecon.c \
selinux/libselinux/src/mapping.c \ selinux/libselinux/src/mapping.c \
selinux/libselinux/src/matchmediacon.c \ selinux/libselinux/src/matchmediacon.c \
selinux/libselinux/src/matchpathcon.c \ selinux/libselinux/src/matchpathcon.c \
selinux/libselinux/src/policyvers.c \ selinux/libselinux/src/policyvers.c \
selinux/libselinux/src/procattr.c \ selinux/libselinux/src/procattr.c \
selinux/libselinux/src/query_user_context.c \ selinux/libselinux/src/query_user_context.c \
selinux/libselinux/src/regex.c \ selinux/libselinux/src/regex.c \
selinux/libselinux/src/reject_unknown.c \ selinux/libselinux/src/reject_unknown.c \
selinux/libselinux/src/selinux_check_securetty_context.c \ selinux/libselinux/src/selinux_check_securetty_context.c \
selinux/libselinux/src/selinux_config.c \ selinux/libselinux/src/selinux_config.c \
selinux/libselinux/src/selinux_restorecon.c \ selinux/libselinux/src/selinux_restorecon.c \
selinux/libselinux/src/sestatus.c \ selinux/libselinux/src/sestatus.c \
selinux/libselinux/src/setenforce.c \ selinux/libselinux/src/setenforce.c \
selinux/libselinux/src/setexecfilecon.c \ selinux/libselinux/src/setexecfilecon.c \
selinux/libselinux/src/setfilecon.c \ selinux/libselinux/src/setfilecon.c \
selinux/libselinux/src/setrans_client.c \ selinux/libselinux/src/setrans_client.c \
selinux/libselinux/src/seusers.c \ selinux/libselinux/src/seusers.c \
selinux/libselinux/src/sha1.c \ selinux/libselinux/src/sha1.c \
selinux/libselinux/src/stringrep.c \ selinux/libselinux/src/stringrep.c \
selinux/libselinux/src/validatetrans.c selinux/libselinux/src/validatetrans.c
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
# libpcre2.a # libpcre2.a
@@ -323,34 +323,34 @@ LOCAL_CFLAGS := -DHAVE_CONFIG_H
LOCAL_C_INCLUDES := $(LIBPCRE2) $(LIBPCRE2)_internal LOCAL_C_INCLUDES := $(LIBPCRE2) $(LIBPCRE2)_internal
LOCAL_EXPORT_C_INCLUDES := $(LIBPCRE2) LOCAL_EXPORT_C_INCLUDES := $(LIBPCRE2)
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
pcre/dist2/src/pcre2_auto_possess.c \ pcre/dist2/src/pcre2_auto_possess.c \
pcre/dist2/src/pcre2_chartables.c \ pcre/dist2/src/pcre2_chartables.c \
pcre/dist2/src/pcre2_compile.c \ pcre/dist2/src/pcre2_compile.c \
pcre/dist2/src/pcre2_config.c \ pcre/dist2/src/pcre2_config.c \
pcre/dist2/src/pcre2_context.c \ pcre/dist2/src/pcre2_context.c \
pcre/dist2/src/pcre2_convert.c \ pcre/dist2/src/pcre2_convert.c \
pcre/dist2/src/pcre2_dfa_match.c \ pcre/dist2/src/pcre2_dfa_match.c \
pcre/dist2/src/pcre2_error.c \ pcre/dist2/src/pcre2_error.c \
pcre/dist2/src/pcre2_extuni.c \ pcre/dist2/src/pcre2_extuni.c \
pcre/dist2/src/pcre2_find_bracket.c \ pcre/dist2/src/pcre2_find_bracket.c \
pcre/dist2/src/pcre2_fuzzsupport.c \ pcre/dist2/src/pcre2_fuzzsupport.c \
pcre/dist2/src/pcre2_jit_compile.c \ pcre/dist2/src/pcre2_jit_compile.c \
pcre/dist2/src/pcre2_maketables.c \ pcre/dist2/src/pcre2_maketables.c \
pcre/dist2/src/pcre2_match.c \ pcre/dist2/src/pcre2_match.c \
pcre/dist2/src/pcre2_match_data.c \ pcre/dist2/src/pcre2_match_data.c \
pcre/dist2/src/pcre2_newline.c \ pcre/dist2/src/pcre2_newline.c \
pcre/dist2/src/pcre2_ord2utf.c \ pcre/dist2/src/pcre2_ord2utf.c \
pcre/dist2/src/pcre2_pattern_info.c \ pcre/dist2/src/pcre2_pattern_info.c \
pcre/dist2/src/pcre2_script_run.c \ pcre/dist2/src/pcre2_script_run.c \
pcre/dist2/src/pcre2_serialize.c \ pcre/dist2/src/pcre2_serialize.c \
pcre/dist2/src/pcre2_string_utils.c \ pcre/dist2/src/pcre2_string_utils.c \
pcre/dist2/src/pcre2_study.c \ pcre/dist2/src/pcre2_study.c \
pcre/dist2/src/pcre2_substitute.c \ pcre/dist2/src/pcre2_substitute.c \
pcre/dist2/src/pcre2_substring.c \ pcre/dist2/src/pcre2_substring.c \
pcre/dist2/src/pcre2_tables.c \ pcre/dist2/src/pcre2_tables.c \
pcre/dist2/src/pcre2_ucd.c \ pcre/dist2/src/pcre2_ucd.c \
pcre/dist2/src/pcre2_valid_utf.c \ pcre/dist2/src/pcre2_valid_utf.c \
pcre/dist2/src/pcre2_xclass.c pcre/dist2/src/pcre2_xclass.c
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
include $(LOCAL_PATH)/mincrypt/Android.mk include $(LOCAL_PATH)/mincrypt/Android.mk

View File

@@ -54,9 +54,9 @@ extern "C" {
* be built with fewer features to minimize code size. * be built with fewer features to minimize code size.
*/ */
enum xz_mode { enum xz_mode {
XZ_SINGLE, XZ_SINGLE,
XZ_PREALLOC, XZ_PREALLOC,
XZ_DYNALLOC XZ_DYNALLOC
}; };
/** /**
@@ -110,15 +110,15 @@ enum xz_mode {
* is used instead of XZ_BUF_ERROR. * is used instead of XZ_BUF_ERROR.
*/ */
enum xz_ret { enum xz_ret {
XZ_OK, XZ_OK,
XZ_STREAM_END, XZ_STREAM_END,
XZ_UNSUPPORTED_CHECK, XZ_UNSUPPORTED_CHECK,
XZ_MEM_ERROR, XZ_MEM_ERROR,
XZ_MEMLIMIT_ERROR, XZ_MEMLIMIT_ERROR,
XZ_FORMAT_ERROR, XZ_FORMAT_ERROR,
XZ_OPTIONS_ERROR, XZ_OPTIONS_ERROR,
XZ_DATA_ERROR, XZ_DATA_ERROR,
XZ_BUF_ERROR XZ_BUF_ERROR
}; };
/** /**
@@ -138,13 +138,13 @@ enum xz_ret {
* the variables in_pos and out_pos are modified by the XZ code. * the variables in_pos and out_pos are modified by the XZ code.
*/ */
struct xz_buf { struct xz_buf {
const uint8_t *in; const uint8_t *in;
size_t in_pos; size_t in_pos;
size_t in_size; size_t in_size;
uint8_t *out; uint8_t *out;
size_t out_pos; size_t out_pos;
size_t out_size; size_t out_size;
}; };
/** /**

View File

@@ -65,7 +65,7 @@ typedef unsigned char bool;
#ifndef __always_inline #ifndef __always_inline
# ifdef __GNUC__ # ifdef __GNUC__
# define __always_inline \ # define __always_inline \
inline __attribute__((__always_inline__)) inline __attribute__((__always_inline__))
# else # else
# define __always_inline inline # define __always_inline inline
# endif # endif
@@ -75,40 +75,40 @@ typedef unsigned char bool;
#ifndef get_unaligned_le32 #ifndef get_unaligned_le32
static inline uint32_t get_unaligned_le32(const uint8_t *buf) static inline uint32_t get_unaligned_le32(const uint8_t *buf)
{ {
return (uint32_t)buf[0] return (uint32_t)buf[0]
| ((uint32_t)buf[1] << 8) | ((uint32_t)buf[1] << 8)
| ((uint32_t)buf[2] << 16) | ((uint32_t)buf[2] << 16)
| ((uint32_t)buf[3] << 24); | ((uint32_t)buf[3] << 24);
} }
#endif #endif
#ifndef get_unaligned_be32 #ifndef get_unaligned_be32
static inline uint32_t get_unaligned_be32(const uint8_t *buf) static inline uint32_t get_unaligned_be32(const uint8_t *buf)
{ {
return (uint32_t)(buf[0] << 24) return (uint32_t)(buf[0] << 24)
| ((uint32_t)buf[1] << 16) | ((uint32_t)buf[1] << 16)
| ((uint32_t)buf[2] << 8) | ((uint32_t)buf[2] << 8)
| (uint32_t)buf[3]; | (uint32_t)buf[3];
} }
#endif #endif
#ifndef put_unaligned_le32 #ifndef put_unaligned_le32
static inline void put_unaligned_le32(uint32_t val, uint8_t *buf) static inline void put_unaligned_le32(uint32_t val, uint8_t *buf)
{ {
buf[0] = (uint8_t)val; buf[0] = (uint8_t)val;
buf[1] = (uint8_t)(val >> 8); buf[1] = (uint8_t)(val >> 8);
buf[2] = (uint8_t)(val >> 16); buf[2] = (uint8_t)(val >> 16);
buf[3] = (uint8_t)(val >> 24); buf[3] = (uint8_t)(val >> 24);
} }
#endif #endif
#ifndef put_unaligned_be32 #ifndef put_unaligned_be32
static inline void put_unaligned_be32(uint32_t val, uint8_t *buf) static inline void put_unaligned_be32(uint32_t val, uint8_t *buf)
{ {
buf[0] = (uint8_t)(val >> 24); buf[0] = (uint8_t)(val >> 24);
buf[1] = (uint8_t)(val >> 16); buf[1] = (uint8_t)(val >> 16);
buf[2] = (uint8_t)(val >> 8); buf[2] = (uint8_t)(val >> 8);
buf[3] = (uint8_t)val; buf[3] = (uint8_t)val;
} }
#endif #endif

View File

@@ -29,31 +29,31 @@ STATIC_RW_DATA uint32_t xz_crc32_table[256];
XZ_EXTERN void xz_crc32_init(void) XZ_EXTERN void xz_crc32_init(void)
{ {
const uint32_t poly = 0xEDB88320; const uint32_t poly = 0xEDB88320;
uint32_t i; uint32_t i;
uint32_t j; uint32_t j;
uint32_t r; uint32_t r;
for (i = 0; i < 256; ++i) { for (i = 0; i < 256; ++i) {
r = i; r = i;
for (j = 0; j < 8; ++j) for (j = 0; j < 8; ++j)
r = (r >> 1) ^ (poly & ~((r & 1) - 1)); r = (r >> 1) ^ (poly & ~((r & 1) - 1));
xz_crc32_table[i] = r; xz_crc32_table[i] = r;
} }
return; return;
} }
XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc) XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
{ {
crc = ~crc; crc = ~crc;
while (size != 0) { while (size != 0) {
crc = xz_crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8); crc = xz_crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8);
--size; --size;
} }
return ~crc; return ~crc;
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -40,18 +40,18 @@
* either short or long repeated match, and NONLIT means any non-literal. * either short or long repeated match, and NONLIT means any non-literal.
*/ */
enum lzma_state { enum lzma_state {
STATE_LIT_LIT, STATE_LIT_LIT,
STATE_MATCH_LIT_LIT, STATE_MATCH_LIT_LIT,
STATE_REP_LIT_LIT, STATE_REP_LIT_LIT,
STATE_SHORTREP_LIT_LIT, STATE_SHORTREP_LIT_LIT,
STATE_MATCH_LIT, STATE_MATCH_LIT,
STATE_REP_LIT, STATE_REP_LIT,
STATE_SHORTREP_LIT, STATE_SHORTREP_LIT,
STATE_LIT_MATCH, STATE_LIT_MATCH,
STATE_LIT_LONGREP, STATE_LIT_LONGREP,
STATE_LIT_SHORTREP, STATE_LIT_SHORTREP,
STATE_NONLIT_MATCH, STATE_NONLIT_MATCH,
STATE_NONLIT_REP STATE_NONLIT_REP
}; };
/* Total number of states */ /* Total number of states */
@@ -63,36 +63,36 @@ enum lzma_state {
/* Indicate that the latest symbol was a literal. */ /* Indicate that the latest symbol was a literal. */
static inline void lzma_state_literal(enum lzma_state *state) static inline void lzma_state_literal(enum lzma_state *state)
{ {
if (*state <= STATE_SHORTREP_LIT_LIT) if (*state <= STATE_SHORTREP_LIT_LIT)
*state = STATE_LIT_LIT; *state = STATE_LIT_LIT;
else if (*state <= STATE_LIT_SHORTREP) else if (*state <= STATE_LIT_SHORTREP)
*state -= 3; *state -= 3;
else else
*state -= 6; *state -= 6;
} }
/* Indicate that the latest symbol was a match. */ /* Indicate that the latest symbol was a match. */
static inline void lzma_state_match(enum lzma_state *state) static inline void lzma_state_match(enum lzma_state *state)
{ {
*state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH; *state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH;
} }
/* Indicate that the latest state was a long repeated match. */ /* Indicate that the latest state was a long repeated match. */
static inline void lzma_state_long_rep(enum lzma_state *state) static inline void lzma_state_long_rep(enum lzma_state *state)
{ {
*state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP; *state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP;
} }
/* Indicate that the latest symbol was a short match. */ /* Indicate that the latest symbol was a short match. */
static inline void lzma_state_short_rep(enum lzma_state *state) static inline void lzma_state_short_rep(enum lzma_state *state)
{ {
*state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP; *state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP;
} }
/* Test if the previous symbol was a literal. */ /* Test if the previous symbol was a literal. */
static inline bool lzma_state_is_literal(enum lzma_state state) static inline bool lzma_state_is_literal(enum lzma_state state)
{ {
return state < LIT_STATES; return state < LIT_STATES;
} }
/* Each literal coder is divided in three sections: /* Each literal coder is divided in three sections:
@@ -146,8 +146,8 @@ static inline bool lzma_state_is_literal(enum lzma_state state)
*/ */
static inline uint32_t lzma_get_dist_state(uint32_t len) static inline uint32_t lzma_get_dist_state(uint32_t len)
{ {
return len < DIST_STATES + MATCH_LEN_MIN return len < DIST_STATES + MATCH_LEN_MIN
? len - MATCH_LEN_MIN : DIST_STATES - 1; ? len - MATCH_LEN_MIN : DIST_STATES - 1;
} }
/* /*

View File

@@ -14,7 +14,7 @@
# include <linux/xz.h> # include <linux/xz.h>
# include <linux/kernel.h> # include <linux/kernel.h>
# include <asm/unaligned.h> # include <asm/unaligned.h>
/* XZ_PREBOOT may be defined only via decompress_unxz.c. */ /* XZ_PREBOOT may be defined only via decompress_unxz.c. */
# ifndef XZ_PREBOOT # ifndef XZ_PREBOOT
# include <linux/slab.h> # include <linux/slab.h>
# include <linux/vmalloc.h> # include <linux/vmalloc.h>
@@ -42,17 +42,17 @@
# endif # endif
# define get_le32(p) le32_to_cpup((const uint32_t *)(p)) # define get_le32(p) le32_to_cpup((const uint32_t *)(p))
#else #else
/* /*
* For userspace builds, use a separate header to define the required * For userspace builds, use a separate header to define the required
* macros and functions. This makes it easier to adapt the code into * macros and functions. This makes it easier to adapt the code into
* different environments and avoids clutter in the Linux kernel tree. * different environments and avoids clutter in the Linux kernel tree.
*/ */
# include "xz_config.h" # include "xz_config.h"
#endif #endif
/* If no specific decoding mode is requested, enable support for all modes. */ /* If no specific decoding mode is requested, enable support for all modes. */
#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \ #if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \
&& !defined(XZ_DEC_DYNALLOC) && !defined(XZ_DEC_DYNALLOC)
# define XZ_DEC_SINGLE # define XZ_DEC_SINGLE
# define XZ_DEC_PREALLOC # define XZ_DEC_PREALLOC
# define XZ_DEC_DYNALLOC # define XZ_DEC_DYNALLOC
@@ -95,9 +95,9 @@
*/ */
#ifndef XZ_DEC_BCJ #ifndef XZ_DEC_BCJ
# if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \ # if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \
|| defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \ || defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \
|| defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \ || defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \
|| defined(XZ_DEC_SPARC) || defined(XZ_DEC_SPARC)
# define XZ_DEC_BCJ # define XZ_DEC_BCJ
# endif # endif
#endif #endif
@@ -107,7 +107,7 @@
* before calling xz_dec_lzma2_run(). * before calling xz_dec_lzma2_run().
*/ */
XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode, XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
uint32_t dict_max); uint32_t dict_max);
/* /*
* Decode the LZMA2 properties (one byte) and reset the decoder. Return * Decode the LZMA2 properties (one byte) and reset the decoder. Return
@@ -116,11 +116,11 @@ XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
* decoder doesn't support. * decoder doesn't support.
*/ */
XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s,
uint8_t props); uint8_t props);
/* Decode raw LZMA2 stream from b->in to b->out. */ /* Decode raw LZMA2 stream from b->in to b->out. */
XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
struct xz_buf *b); struct xz_buf *b);
/* Free the memory allocated for the LZMA2 decoder. */ /* Free the memory allocated for the LZMA2 decoder. */
XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s); XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s);
@@ -146,8 +146,8 @@ XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id);
* must be called directly. * must be called directly.
*/ */
XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s, XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
struct xz_dec_lzma2 *lzma2, struct xz_dec_lzma2 *lzma2,
struct xz_buf *b); struct xz_buf *b);
/* Free the memory allocated for the BCJ filters. */ /* Free the memory allocated for the BCJ filters. */
#define xz_dec_bcj_end(s) kfree(s) #define xz_dec_bcj_end(s) kfree(s)

View File

@@ -14,7 +14,7 @@
# include <linux/crc32.h> # include <linux/crc32.h>
# undef crc32 # undef crc32
# define xz_crc32(buf, size, crc) \ # define xz_crc32(buf, size, crc) \
(~crc32_le(~(uint32_t)(crc), buf, size)) (~crc32_le(~(uint32_t)(crc), buf, size))
#endif #endif
/* /*
@@ -50,10 +50,10 @@ typedef uint64_t vli_type;
/* Integrity Check types */ /* Integrity Check types */
enum xz_check { enum xz_check {
XZ_CHECK_NONE = 0, XZ_CHECK_NONE = 0,
XZ_CHECK_CRC32 = 1, XZ_CHECK_CRC32 = 1,
XZ_CHECK_CRC64 = 4, XZ_CHECK_CRC64 = 4,
XZ_CHECK_SHA256 = 10 XZ_CHECK_SHA256 = 10
}; };
/* Maximum possible Check ID */ /* Maximum possible Check ID */

View File

@@ -8,35 +8,35 @@
// Daemon command codes // Daemon command codes
enum { enum {
START_DAEMON, START_DAEMON,
SUPERUSER, SUPERUSER,
CHECK_VERSION, CHECK_VERSION,
CHECK_VERSION_CODE, CHECK_VERSION_CODE,
POST_FS_DATA, POST_FS_DATA,
LATE_START, LATE_START,
BOOT_COMPLETE, BOOT_COMPLETE,
MAGISKHIDE, MAGISKHIDE,
SQLITE_CMD, SQLITE_CMD,
REMOVE_MODULES, REMOVE_MODULES,
GET_PATH, GET_PATH,
DAEMON_CODE_END, DAEMON_CODE_END,
}; };
// Return codes for daemon // Return codes for daemon
enum { enum {
DAEMON_ERROR = -1, DAEMON_ERROR = -1,
DAEMON_SUCCESS = 0, DAEMON_SUCCESS = 0,
ROOT_REQUIRED, ROOT_REQUIRED,
DAEMON_LAST DAEMON_LAST
}; };
// Daemon state // Daemon state
enum { enum {
STATE_NONE, STATE_NONE,
STATE_POST_FS_DATA, STATE_POST_FS_DATA,
STATE_POST_FS_DATA_DONE, STATE_POST_FS_DATA_DONE,
STATE_LATE_START_DONE, STATE_LATE_START_DONE,
STATE_BOOT_COMPLETE STATE_BOOT_COMPLETE
}; };
extern int SDK_INT; extern int SDK_INT;

View File

@@ -9,25 +9,25 @@
template <class T, size_t num> template <class T, size_t num>
class db_data_base { class db_data_base {
public: public:
T& operator [](std::string_view key) { T& operator [](std::string_view key) {
return data[getKeyIdx(key)]; return data[getKeyIdx(key)];
} }
const T& operator [](std::string_view key) const { const T& operator [](std::string_view key) const {
return data[getKeyIdx(key)]; return data[getKeyIdx(key)];
} }
T& operator [](int key) { T& operator [](int key) {
return data[key]; return data[key];
} }
const T& operator [](int key) const { const T& operator [](int key) const {
return data[key]; return data[key];
} }
protected: protected:
T data[num + 1]; T data[num + 1];
virtual int getKeyIdx(std::string_view key) const = 0; virtual int getKeyIdx(std::string_view key) const = 0;
}; };
/*************** /***************
@@ -46,40 +46,40 @@ protected:
// Settings keys // Settings keys
enum { enum {
ROOT_ACCESS = 0, ROOT_ACCESS = 0,
SU_MULTIUSER_MODE, SU_MULTIUSER_MODE,
SU_MNT_NS, SU_MNT_NS,
HIDE_CONFIG HIDE_CONFIG
}; };
// Values for root_access // Values for root_access
enum { enum {
ROOT_ACCESS_DISABLED = 0, ROOT_ACCESS_DISABLED = 0,
ROOT_ACCESS_APPS_ONLY, ROOT_ACCESS_APPS_ONLY,
ROOT_ACCESS_ADB_ONLY, ROOT_ACCESS_ADB_ONLY,
ROOT_ACCESS_APPS_AND_ADB ROOT_ACCESS_APPS_AND_ADB
}; };
// Values for multiuser_mode // Values for multiuser_mode
enum { enum {
MULTIUSER_MODE_OWNER_ONLY = 0, MULTIUSER_MODE_OWNER_ONLY = 0,
MULTIUSER_MODE_OWNER_MANAGED, MULTIUSER_MODE_OWNER_MANAGED,
MULTIUSER_MODE_USER MULTIUSER_MODE_USER
}; };
// Values for mnt_ns // Values for mnt_ns
enum { enum {
NAMESPACE_MODE_GLOBAL = 0, NAMESPACE_MODE_GLOBAL = 0,
NAMESPACE_MODE_REQUESTER, NAMESPACE_MODE_REQUESTER,
NAMESPACE_MODE_ISOLATE NAMESPACE_MODE_ISOLATE
}; };
class db_settings : public db_data_base<int, DB_SETTINGS_NUM> { class db_settings : public db_data_base<int, DB_SETTINGS_NUM> {
public: public:
db_settings(); db_settings();
protected: protected:
int getKeyIdx(std::string_view key) const override; int getKeyIdx(std::string_view key) const override;
}; };
/************** /**************
@@ -95,12 +95,12 @@ protected:
// Strings keys // Strings keys
enum { enum {
SU_MANAGER = 0 SU_MANAGER = 0
}; };
class db_strings : public db_data_base<std::string, DB_STRING_NUM> { class db_strings : public db_data_base<std::string, DB_STRING_NUM> {
protected: protected:
int getKeyIdx(std::string_view key) const override; int getKeyIdx(std::string_view key) const override;
}; };
/************* /*************
@@ -108,15 +108,15 @@ protected:
*************/ *************/
typedef enum { typedef enum {
QUERY = 0, QUERY = 0,
DENY = 1, DENY = 1,
ALLOW = 2, ALLOW = 2,
} policy_t; } policy_t;
struct su_access { struct su_access {
policy_t policy; policy_t policy;
int log; int log;
int notify; int notify;
}; };
#define DEFAULT_SU_ACCESS (su_access) { \ #define DEFAULT_SU_ACCESS (su_access) { \

View File

@@ -9,52 +9,52 @@ struct policydb;
class sepolicy { class sepolicy {
public: public:
typedef const char * c_str; typedef const char * c_str;
~sepolicy(); ~sepolicy();
// Public static factory functions // Public static factory functions
static sepolicy *from_file(c_str file); static sepolicy *from_file(c_str file);
static sepolicy *from_split(); static sepolicy *from_split();
static sepolicy *compile_split(); static sepolicy *compile_split();
// External APIs // External APIs
bool to_file(c_str file); bool to_file(c_str file);
void parse_statement(c_str stmt); void parse_statement(c_str stmt);
void load_rule_file(c_str file); void load_rule_file(c_str file);
// Operation on types // Operation on types
bool type(c_str name, c_str attr); bool type(c_str name, c_str attr);
bool attribute(c_str name); bool attribute(c_str name);
bool permissive(c_str type); bool permissive(c_str type);
bool enforce(c_str type); bool enforce(c_str type);
bool typeattribute(c_str type, c_str attr); bool typeattribute(c_str type, c_str attr);
bool exists(c_str type); bool exists(c_str type);
// Access vector rules // Access vector rules
bool allow(c_str src, c_str tgt, c_str cls, c_str perm); bool allow(c_str src, c_str tgt, c_str cls, c_str perm);
bool deny(c_str src, c_str tgt, c_str cls, c_str perm); bool deny(c_str src, c_str tgt, c_str cls, c_str perm);
bool auditallow(c_str src, c_str tgt, c_str cls, c_str perm); bool auditallow(c_str src, c_str tgt, c_str cls, c_str perm);
bool dontaudit(c_str src, c_str tgt, c_str cls, c_str perm); bool dontaudit(c_str src, c_str tgt, c_str cls, c_str perm);
// Extended permissions access vector rules // Extended permissions access vector rules
bool allowxperm(c_str src, c_str tgt, c_str cls, c_str range); bool allowxperm(c_str src, c_str tgt, c_str cls, c_str range);
bool auditallowxperm(c_str src, c_str tgt, c_str cls, c_str range); bool auditallowxperm(c_str src, c_str tgt, c_str cls, c_str range);
bool dontauditxperm(c_str src, c_str tgt, c_str cls, c_str range); bool dontauditxperm(c_str src, c_str tgt, c_str cls, c_str range);
// Type rules // Type rules
bool type_transition(c_str src, c_str tgt, c_str cls, c_str def, c_str obj = nullptr); bool type_transition(c_str src, c_str tgt, c_str cls, c_str def, c_str obj = nullptr);
bool type_change(c_str src, c_str tgt, c_str cls, c_str def); bool type_change(c_str src, c_str tgt, c_str cls, c_str def);
bool type_member(c_str src, c_str tgt, c_str cls, c_str def); bool type_member(c_str src, c_str tgt, c_str cls, c_str def);
// File system labeling // File system labeling
bool genfscon(c_str fs_name, c_str path, c_str ctx); bool genfscon(c_str fs_name, c_str path, c_str ctx);
// Magisk // Magisk
void magisk_rules(); void magisk_rules();
// Deprecate // Deprecate
bool create(c_str name) { return type(name, "domain"); } bool create(c_str name) { return type(name, "domain"); }
protected: protected:
policydb *db; policydb *db;
}; };

View File

@@ -6,6 +6,6 @@
int setprop(const char *name, const char *value, bool prop_svc = true); int setprop(const char *name, const char *value, bool prop_svc = true);
std::string getprop(const char *name, bool persist = false); std::string getprop(const char *name, bool persist = false);
void getprops(void (*callback)(const char *, const char *, void *), void getprops(void (*callback)(const char *, const char *, void *),
void *cookie = nullptr, bool persist = false); void *cookie = nullptr, bool persist = false);
int delprop(const char *name, bool persist = false); int delprop(const char *name, bool persist = false);
void load_prop_file(const char *filename, bool prop_svc = true); void load_prop_file(const char *filename, bool prop_svc = true);

View File

@@ -13,121 +13,121 @@ using namespace std;
template<typename Func> template<typename Func>
static void parse_cmdline(const Func &fn) { static void parse_cmdline(const Func &fn) {
char cmdline[4096]; char cmdline[4096];
int fd = xopen("/proc/cmdline", O_RDONLY | O_CLOEXEC); int fd = xopen("/proc/cmdline", O_RDONLY | O_CLOEXEC);
cmdline[read(fd, cmdline, sizeof(cmdline))] = '\0'; cmdline[read(fd, cmdline, sizeof(cmdline))] = '\0';
close(fd); close(fd);
char *tok, *eql, *tmp, *saveptr; char *tok, *eql, *tmp, *saveptr;
saveptr = cmdline; saveptr = cmdline;
while ((tok = strtok_r(nullptr, " \n", &saveptr)) != nullptr) { while ((tok = strtok_r(nullptr, " \n", &saveptr)) != nullptr) {
eql = strchr(tok, '='); eql = strchr(tok, '=');
if (eql) { if (eql) {
*eql = '\0'; *eql = '\0';
if (eql[1] == '"') { if (eql[1] == '"') {
tmp = strchr(saveptr, '"'); tmp = strchr(saveptr, '"');
if (tmp != nullptr) { if (tmp != nullptr) {
*tmp = '\0'; *tmp = '\0';
saveptr[-1] = ' '; saveptr[-1] = ' ';
saveptr = tmp + 1; saveptr = tmp + 1;
eql++; eql++;
} }
} }
fn(tok, eql + 1); fn(tok, eql + 1);
} else { } else {
fn(tok, ""); fn(tok, "");
} }
} }
} }
#define test_bit(bit, array) (array[bit / 8] & (1 << (bit % 8))) #define test_bit(bit, array) (array[bit / 8] & (1 << (bit % 8)))
static bool check_key_combo() { static bool check_key_combo() {
uint8_t bitmask[(KEY_MAX + 1) / 8]; uint8_t bitmask[(KEY_MAX + 1) / 8];
vector<int> events; vector<int> events;
constexpr const char *name = "/event"; constexpr const char *name = "/event";
for (int minor = 64; minor < 96; ++minor) { for (int minor = 64; minor < 96; ++minor) {
if (xmknod(name, S_IFCHR | 0444, makedev(13, minor))) if (xmknod(name, S_IFCHR | 0444, makedev(13, minor)))
continue; continue;
int fd = open(name, O_RDONLY | O_CLOEXEC); int fd = open(name, O_RDONLY | O_CLOEXEC);
unlink(name); unlink(name);
if (fd < 0) if (fd < 0)
continue; continue;
memset(bitmask, 0, sizeof(bitmask)); memset(bitmask, 0, sizeof(bitmask));
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask); ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask);
if (test_bit(KEY_VOLUMEUP, bitmask)) if (test_bit(KEY_VOLUMEUP, bitmask))
events.push_back(fd); events.push_back(fd);
else else
close(fd); close(fd);
} }
if (events.empty()) if (events.empty())
return false; return false;
run_finally fin([&]{ std::for_each(events.begin(), events.end(), close); }); run_finally fin([&]{ std::for_each(events.begin(), events.end(), close); });
// Return true if volume up key is held for more than 3 seconds // Return true if volume up key is held for more than 3 seconds
int count = 0; int count = 0;
for (int i = 0; i < 500; ++i) { for (int i = 0; i < 500; ++i) {
for (const int &fd : events) { for (const int &fd : events) {
memset(bitmask, 0, sizeof(bitmask)); memset(bitmask, 0, sizeof(bitmask));
ioctl(fd, EVIOCGKEY(sizeof(bitmask)), bitmask); ioctl(fd, EVIOCGKEY(sizeof(bitmask)), bitmask);
if (test_bit(KEY_VOLUMEUP, bitmask)) { if (test_bit(KEY_VOLUMEUP, bitmask)) {
count++; count++;
break; break;
} }
} }
if (count >= 300) { if (count >= 300) {
LOGD("KEY_VOLUMEUP detected: disable system-as-root\n"); LOGD("KEY_VOLUMEUP detected: disable system-as-root\n");
return true; return true;
} }
// Check every 10ms // Check every 10ms
usleep(10000); usleep(10000);
} }
return false; return false;
} }
static FILE *kmsg; static FILE *kmsg;
static char kmsg_buf[4096]; static char kmsg_buf[4096];
static int vprintk(const char *fmt, va_list ap) { static int vprintk(const char *fmt, va_list ap) {
vsnprintf(kmsg_buf + 12, sizeof(kmsg_buf) - 12, fmt, ap); vsnprintf(kmsg_buf + 12, sizeof(kmsg_buf) - 12, fmt, ap);
return fprintf(kmsg, "%s", kmsg_buf); return fprintf(kmsg, "%s", kmsg_buf);
} }
void setup_klog() { void setup_klog() {
// Shut down first 3 fds // Shut down first 3 fds
int fd; int fd;
if (access("/dev/null", W_OK) == 0) { if (access("/dev/null", W_OK) == 0) {
fd = xopen("/dev/null", O_RDWR | O_CLOEXEC); fd = xopen("/dev/null", O_RDWR | O_CLOEXEC);
} else { } else {
mknod("/null", S_IFCHR | 0666, makedev(1, 3)); mknod("/null", S_IFCHR | 0666, makedev(1, 3));
fd = xopen("/null", O_RDWR | O_CLOEXEC); fd = xopen("/null", O_RDWR | O_CLOEXEC);
unlink("/null"); unlink("/null");
} }
xdup3(fd, STDIN_FILENO, O_CLOEXEC); xdup3(fd, STDIN_FILENO, O_CLOEXEC);
xdup3(fd, STDOUT_FILENO, O_CLOEXEC); xdup3(fd, STDOUT_FILENO, O_CLOEXEC);
xdup3(fd, STDERR_FILENO, O_CLOEXEC); xdup3(fd, STDERR_FILENO, O_CLOEXEC);
if (fd > STDERR_FILENO) if (fd > STDERR_FILENO)
close(fd); close(fd);
if (access("/dev/kmsg", W_OK) == 0) { if (access("/dev/kmsg", W_OK) == 0) {
fd = xopen("/dev/kmsg", O_WRONLY | O_CLOEXEC); fd = xopen("/dev/kmsg", O_WRONLY | O_CLOEXEC);
} else { } else {
mknod("/kmsg", S_IFCHR | 0666, makedev(1, 11)); mknod("/kmsg", S_IFCHR | 0666, makedev(1, 11));
fd = xopen("/kmsg", O_WRONLY | O_CLOEXEC); fd = xopen("/kmsg", O_WRONLY | O_CLOEXEC);
unlink("/kmsg"); unlink("/kmsg");
} }
kmsg = fdopen(fd, "w"); kmsg = fdopen(fd, "w");
setbuf(kmsg, nullptr); setbuf(kmsg, nullptr);
log_cb.d = log_cb.i = log_cb.w = log_cb.e = vprintk; log_cb.d = log_cb.i = log_cb.w = log_cb.e = vprintk;
log_cb.ex = nop_ex; log_cb.ex = nop_ex;
strcpy(kmsg_buf, "magiskinit: "); strcpy(kmsg_buf, "magiskinit: ");
// Disable kmsg rate limiting // Disable kmsg rate limiting
if (FILE *rate = fopen("/proc/sys/kernel/printk_devkmsg", "w")) { if (FILE *rate = fopen("/proc/sys/kernel/printk_devkmsg", "w")) {
fprintf(rate, "on\n"); fprintf(rate, "on\n");
fclose(rate); fclose(rate);
} }
} }
#define read_dt(name, key) \ #define read_dt(name, key) \
@@ -141,78 +141,78 @@ if (access(file_name, R_OK) == 0){ \
} }
void load_kernel_info(cmdline *cmd) { void load_kernel_info(cmdline *cmd) {
// Get kernel data using procfs and sysfs // Get kernel data using procfs and sysfs
xmkdir("/proc", 0755); xmkdir("/proc", 0755);
xmount("proc", "/proc", "proc", 0, nullptr); xmount("proc", "/proc", "proc", 0, nullptr);
xmkdir("/sys", 0755); xmkdir("/sys", 0755);
xmount("sysfs", "/sys", "sysfs", 0, nullptr); xmount("sysfs", "/sys", "sysfs", 0, nullptr);
// Log to kernel // Log to kernel
setup_klog(); setup_klog();
parse_cmdline([=](string_view key, const char *value) -> void { parse_cmdline([=](string_view key, const char *value) -> void {
if (key == "androidboot.slot_suffix") { if (key == "androidboot.slot_suffix") {
strcpy(cmd->slot, value); strcpy(cmd->slot, value);
} else if (key == "androidboot.slot") { } else if (key == "androidboot.slot") {
cmd->slot[0] = '_'; cmd->slot[0] = '_';
strcpy(cmd->slot + 1, value); strcpy(cmd->slot + 1, value);
} else if (key == "skip_initramfs") { } else if (key == "skip_initramfs") {
cmd->skip_initramfs = true; cmd->skip_initramfs = true;
} else if (key == "androidboot.force_normal_boot") { } else if (key == "androidboot.force_normal_boot") {
cmd->force_normal_boot = value[0] == '1'; cmd->force_normal_boot = value[0] == '1';
} else if (key == "rootwait") { } else if (key == "rootwait") {
cmd->rootwait = true; cmd->rootwait = true;
} else if (key == "androidboot.android_dt_dir") { } else if (key == "androidboot.android_dt_dir") {
strcpy(cmd->dt_dir, value); strcpy(cmd->dt_dir, value);
} else if (key == "androidboot.hardware") { } else if (key == "androidboot.hardware") {
strcpy(cmd->hardware, value); strcpy(cmd->hardware, value);
} else if (key == "androidboot.hardware.platform") { } else if (key == "androidboot.hardware.platform") {
strcpy(cmd->hardware_plat, value); strcpy(cmd->hardware_plat, value);
} else if (key == "androidboot.fstab_suffix") { } else if (key == "androidboot.fstab_suffix") {
strcpy(cmd->fstab_suffix, value); strcpy(cmd->fstab_suffix, value);
} }
}); });
LOGD("Kernel cmdline info:\n"); LOGD("Kernel cmdline info:\n");
LOGD("skip_initramfs=[%d]\n", cmd->skip_initramfs); LOGD("skip_initramfs=[%d]\n", cmd->skip_initramfs);
LOGD("force_normal_boot=[%d]\n", cmd->force_normal_boot); LOGD("force_normal_boot=[%d]\n", cmd->force_normal_boot);
LOGD("rootwait=[%d]\n", cmd->rootwait); LOGD("rootwait=[%d]\n", cmd->rootwait);
LOGD("slot=[%s]\n", cmd->slot); LOGD("slot=[%s]\n", cmd->slot);
LOGD("dt_dir=[%s]\n", cmd->dt_dir); LOGD("dt_dir=[%s]\n", cmd->dt_dir);
LOGD("fstab_suffix=[%s]\n", cmd->fstab_suffix); LOGD("fstab_suffix=[%s]\n", cmd->fstab_suffix);
LOGD("hardware=[%s]\n", cmd->hardware); LOGD("hardware=[%s]\n", cmd->hardware);
LOGD("hardware.platform=[%s]\n", cmd->hardware_plat); LOGD("hardware.platform=[%s]\n", cmd->hardware_plat);
parse_prop_file("/.backup/.magisk", [=](auto key, auto value) -> bool { parse_prop_file("/.backup/.magisk", [=](auto key, auto value) -> bool {
if (key == "RECOVERYMODE" && value == "true") { if (key == "RECOVERYMODE" && value == "true") {
LOGD("Running in recovery mode, waiting for key...\n"); LOGD("Running in recovery mode, waiting for key...\n");
cmd->skip_initramfs = !check_key_combo(); cmd->skip_initramfs = !check_key_combo();
return false; return false;
} }
return true; return true;
}); });
if (cmd->dt_dir[0] == '\0') if (cmd->dt_dir[0] == '\0')
strcpy(cmd->dt_dir, DEFAULT_DT_DIR); strcpy(cmd->dt_dir, DEFAULT_DT_DIR);
char file_name[128]; char file_name[128];
read_dt("fstab_suffix", fstab_suffix) read_dt("fstab_suffix", fstab_suffix)
read_dt("hardware", hardware) read_dt("hardware", hardware)
read_dt("hardware.platform", hardware_plat) read_dt("hardware.platform", hardware_plat)
LOGD("Device tree info:\n"); LOGD("Device tree info:\n");
LOGD("dt_dir=[%s]\n", cmd->dt_dir); LOGD("dt_dir=[%s]\n", cmd->dt_dir);
LOGD("fstab_suffix=[%s]\n", cmd->fstab_suffix); LOGD("fstab_suffix=[%s]\n", cmd->fstab_suffix);
LOGD("hardware=[%s]\n", cmd->hardware); LOGD("hardware=[%s]\n", cmd->hardware);
LOGD("hardware.platform=[%s]\n", cmd->hardware_plat); LOGD("hardware.platform=[%s]\n", cmd->hardware_plat);
} }
bool check_two_stage() { bool check_two_stage() {
if (access("/apex", F_OK) == 0) if (access("/apex", F_OK) == 0)
return true; return true;
if (access("/system/bin/init", F_OK) == 0) if (access("/system/bin/init", F_OK) == 0)
return true; return true;
// If we still have no indication, parse the original init and see what's up // If we still have no indication, parse the original init and see what's up
auto init = raw_data::mmap_ro("/.backup/init"); auto init = raw_data::mmap_ro("/.backup/init");
return init.contains("selinux_setup"); return init.contains("selinux_setup");
} }

View File

@@ -24,163 +24,163 @@ using namespace std;
#define ENABLE_TEST 0 #define ENABLE_TEST 0
constexpr int (*init_applet_main[])(int, char *[]) = constexpr int (*init_applet_main[])(int, char *[]) =
{ magiskpolicy_main, magiskpolicy_main, nullptr }; { magiskpolicy_main, magiskpolicy_main, nullptr };
static bool unxz(int fd, const uint8_t *buf, size_t size) { static bool unxz(int fd, const uint8_t *buf, size_t size) {
uint8_t out[8192]; uint8_t out[8192];
xz_crc32_init(); xz_crc32_init();
struct xz_dec *dec = xz_dec_init(XZ_DYNALLOC, 1 << 26); struct xz_dec *dec = xz_dec_init(XZ_DYNALLOC, 1 << 26);
struct xz_buf b = { struct xz_buf b = {
.in = buf, .in = buf,
.in_pos = 0, .in_pos = 0,
.in_size = size, .in_size = size,
.out = out, .out = out,
.out_pos = 0, .out_pos = 0,
.out_size = sizeof(out) .out_size = sizeof(out)
}; };
enum xz_ret ret; enum xz_ret ret;
do { do {
ret = xz_dec_run(dec, &b); ret = xz_dec_run(dec, &b);
if (ret != XZ_OK && ret != XZ_STREAM_END) if (ret != XZ_OK && ret != XZ_STREAM_END)
return false; return false;
write(fd, out, b.out_pos); write(fd, out, b.out_pos);
b.out_pos = 0; b.out_pos = 0;
} while (b.in_pos != size); } while (b.in_pos != size);
return true; return true;
} }
int dump_magisk(const char *path, mode_t mode) { int dump_magisk(const char *path, mode_t mode) {
int fd = xopen(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode); int fd = xopen(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
if (fd < 0) if (fd < 0)
return 1; return 1;
if (!unxz(fd, magisk_xz, sizeof(magisk_xz))) if (!unxz(fd, magisk_xz, sizeof(magisk_xz)))
return 1; return 1;
close(fd); close(fd);
return 0; return 0;
} }
static int dump_manager(const char *path, mode_t mode) { static int dump_manager(const char *path, mode_t mode) {
int fd = xopen(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode); int fd = xopen(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
if (fd < 0) if (fd < 0)
return 1; return 1;
if (!unxz(fd, manager_xz, sizeof(manager_xz))) if (!unxz(fd, manager_xz, sizeof(manager_xz)))
return 1; return 1;
close(fd); close(fd);
return 0; return 0;
} }
class RecoveryInit : public BaseInit { class RecoveryInit : public BaseInit {
public: public:
RecoveryInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {} RecoveryInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {}
void start() override { void start() override {
LOGD("Ramdisk is recovery, abort\n"); LOGD("Ramdisk is recovery, abort\n");
rename("/.backup/init", "/init"); rename("/.backup/init", "/init");
rm_rf("/.backup"); rm_rf("/.backup");
exec_init(); exec_init();
} }
}; };
#if ENABLE_TEST #if ENABLE_TEST
class TestInit : public BaseInit { class TestInit : public BaseInit {
public: public:
TestInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {}; TestInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {};
void start() override { void start() override {
// Place init tests here // Place init tests here
} }
}; };
static int test_main(int argc, char *argv[]) { static int test_main(int argc, char *argv[]) {
// Log to console // Log to console
cmdline_logging(); cmdline_logging();
log_cb.ex = nop_ex; log_cb.ex = nop_ex;
// Switch to isolate namespace // Switch to isolate namespace
xunshare(CLONE_NEWNS); xunshare(CLONE_NEWNS);
xmount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr); xmount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr);
// Unmount everything in reverse // Unmount everything in reverse
vector<string> mounts; vector<string> mounts;
parse_mnt("/proc/mounts", [&](mntent *me) { parse_mnt("/proc/mounts", [&](mntent *me) {
if (me->mnt_dir != "/"sv) if (me->mnt_dir != "/"sv)
mounts.emplace_back(me->mnt_dir); mounts.emplace_back(me->mnt_dir);
return true; return true;
}); });
for (auto &m : reversed(mounts)) for (auto &m : reversed(mounts))
xumount(m.data()); xumount(m.data());
// chroot jail // chroot jail
chdir(dirname(argv[0])); chdir(dirname(argv[0]));
chroot("."); chroot(".");
chdir("/"); chdir("/");
cmdline cmd{}; cmdline cmd{};
load_kernel_info(&cmd); load_kernel_info(&cmd);
auto init = make_unique<TestInit>(argv, &cmd); auto init = make_unique<TestInit>(argv, &cmd);
init->start(); init->start();
return 1; return 1;
} }
#endif // ENABLE_TEST #endif // ENABLE_TEST
static int magisk_proxy_main(int argc, char *argv[]) { static int magisk_proxy_main(int argc, char *argv[]) {
setup_klog(); setup_klog();
auto init = make_unique<MagiskProxy>(argv); auto init = make_unique<MagiskProxy>(argv);
init->start(); init->start();
return 1; return 1;
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
umask(0); umask(0);
auto name = basename(argv[0]); auto name = basename(argv[0]);
if (name == "magisk"sv) if (name == "magisk"sv)
return magisk_proxy_main(argc, argv); return magisk_proxy_main(argc, argv);
for (int i = 0; init_applet[i]; ++i) { for (int i = 0; init_applet[i]; ++i) {
if (strcmp(name, init_applet[i]) == 0) if (strcmp(name, init_applet[i]) == 0)
return (*init_applet_main[i])(argc, argv); return (*init_applet_main[i])(argc, argv);
} }
#if ENABLE_TEST #if ENABLE_TEST
if (getenv("INIT_TEST") != nullptr) if (getenv("INIT_TEST") != nullptr)
return test_main(argc, argv); return test_main(argc, argv);
#endif #endif
if (argc > 1 && argv[1] == "-x"sv) { if (argc > 1 && argv[1] == "-x"sv) {
if (argv[2] == "magisk"sv) if (argv[2] == "magisk"sv)
return dump_magisk(argv[3], 0755); return dump_magisk(argv[3], 0755);
else if (argv[2] == "manager"sv) else if (argv[2] == "manager"sv)
return dump_manager(argv[3], 0644); return dump_manager(argv[3], 0644);
} }
if (getpid() != 1) if (getpid() != 1)
return 1; return 1;
BaseInit *init; BaseInit *init;
cmdline cmd{}; cmdline cmd{};
if (argc > 1 && argv[1] == "selinux_setup"sv) { if (argc > 1 && argv[1] == "selinux_setup"sv) {
setup_klog(); setup_klog();
init = new SecondStageInit(argv); init = new SecondStageInit(argv);
} else { } else {
// This will also mount /sys and /proc // This will also mount /sys and /proc
load_kernel_info(&cmd); load_kernel_info(&cmd);
if (cmd.skip_initramfs) { if (cmd.skip_initramfs) {
init = new SARInit(argv, &cmd); init = new SARInit(argv, &cmd);
} else { } else {
if (cmd.force_normal_boot) if (cmd.force_normal_boot)
init = new FirstStageInit(argv, &cmd); init = new FirstStageInit(argv, &cmd);
else if (access("/sbin/recovery", F_OK) == 0 || access("/system/bin/recovery", F_OK) == 0) else if (access("/sbin/recovery", F_OK) == 0 || access("/system/bin/recovery", F_OK) == 0)
init = new RecoveryInit(argv, &cmd); init = new RecoveryInit(argv, &cmd);
else if (check_two_stage()) else if (check_two_stage())
init = new FirstStageInit(argv, &cmd); init = new FirstStageInit(argv, &cmd);
else else
init = new RootFSInit(argv, &cmd); init = new RootFSInit(argv, &cmd);
} }
} }
// Run the main routine // Run the main routine
init->start(); init->start();
exit(1); exit(1);
} }

View File

@@ -3,27 +3,27 @@
#include "raw_data.hpp" #include "raw_data.hpp"
struct cmdline { struct cmdline {
bool skip_initramfs; bool skip_initramfs;
bool force_normal_boot; bool force_normal_boot;
bool rootwait; bool rootwait;
char slot[3]; char slot[3];
char dt_dir[64]; char dt_dir[64];
char fstab_suffix[32]; char fstab_suffix[32];
char hardware[32]; char hardware[32];
char hardware_plat[32]; char hardware_plat[32];
}; };
struct fstab_entry { struct fstab_entry {
std::string dev; std::string dev;
std::string mnt_point; std::string mnt_point;
std::string type; std::string type;
std::string mnt_flags; std::string mnt_flags;
std::string fsmgr_flags; std::string fsmgr_flags;
fstab_entry() = default; fstab_entry() = default;
fstab_entry(const fstab_entry &) = delete; fstab_entry(const fstab_entry &) = delete;
fstab_entry(fstab_entry &&) = default; fstab_entry(fstab_entry &&) = default;
void to_file(FILE *fp); void to_file(FILE *fp);
}; };
#define INIT_SOCKET "MAGISKINIT" #define INIT_SOCKET "MAGISKINIT"
@@ -40,41 +40,41 @@ void setup_klog();
class BaseInit { class BaseInit {
protected: protected:
cmdline *cmd; cmdline *cmd;
char **argv; char **argv;
std::vector<std::string> mount_list; std::vector<std::string> mount_list;
[[noreturn]] void exec_init(); [[noreturn]] void exec_init();
void read_dt_fstab(std::vector<fstab_entry> &fstab); void read_dt_fstab(std::vector<fstab_entry> &fstab);
public: public:
BaseInit(char *argv[], cmdline *cmd) : cmd(cmd), argv(argv), mount_list{"/sys", "/proc"} {} BaseInit(char *argv[], cmdline *cmd) : cmd(cmd), argv(argv), mount_list{"/sys", "/proc"} {}
virtual ~BaseInit() = default; virtual ~BaseInit() = default;
virtual void start() = 0; virtual void start() = 0;
}; };
class MagiskInit : public BaseInit { class MagiskInit : public BaseInit {
protected: protected:
auto_data<HEAP> self; auto_data<HEAP> self;
auto_data<HEAP> config; auto_data<HEAP> config;
std::string custom_rules_dir; std::string custom_rules_dir;
void mount_with_dt(); void mount_with_dt();
bool patch_sepolicy(const char *file); bool patch_sepolicy(const char *file);
void setup_tmp(const char *path); void setup_tmp(const char *path);
void mount_rules_dir(const char *dev_base, const char *mnt_base); void mount_rules_dir(const char *dev_base, const char *mnt_base);
public: public:
MagiskInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {} MagiskInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {}
}; };
class SARBase : public MagiskInit { class SARBase : public MagiskInit {
protected: protected:
std::vector<raw_file> overlays; std::vector<raw_file> overlays;
void backup_files(); void backup_files();
void patch_rootdir(); void patch_rootdir();
void mount_system_root(); void mount_system_root();
public: public:
SARBase(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {} SARBase(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {}
}; };
/*************** /***************
@@ -83,31 +83,31 @@ public:
class FirstStageInit : public BaseInit { class FirstStageInit : public BaseInit {
private: private:
void prepare(); void prepare();
public: public:
FirstStageInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) { FirstStageInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {
LOGD("%s\n", __FUNCTION__); LOGD("%s\n", __FUNCTION__);
}; };
void start() override { void start() override {
prepare(); prepare();
exec_init(); exec_init();
} }
}; };
class SecondStageInit : public SARBase { class SecondStageInit : public SARBase {
private: private:
void prepare(); void prepare();
public: public:
SecondStageInit(char *argv[]) : SARBase(argv, nullptr) { SecondStageInit(char *argv[]) : SARBase(argv, nullptr) {
LOGD("%s\n", __FUNCTION__); LOGD("%s\n", __FUNCTION__);
// Do not unmount /sys and /proc // Do not unmount /sys and /proc
mount_list.clear(); mount_list.clear();
}; };
void start() override { void start() override {
prepare(); prepare();
patch_rootdir(); patch_rootdir();
exec_init(); exec_init();
} }
}; };
/************* /*************
@@ -116,22 +116,22 @@ public:
class SARInit : public SARBase { class SARInit : public SARBase {
private: private:
bool is_two_stage; bool is_two_stage;
void early_mount(); void early_mount();
void first_stage_prep(); void first_stage_prep();
public: public:
SARInit(char *argv[], cmdline *cmd) : SARBase(argv, cmd), is_two_stage(false) { SARInit(char *argv[], cmdline *cmd) : SARBase(argv, cmd), is_two_stage(false) {
LOGD("%s\n", __FUNCTION__); LOGD("%s\n", __FUNCTION__);
}; };
void start() override { void start() override {
early_mount(); early_mount();
if (is_two_stage) if (is_two_stage)
first_stage_prep(); first_stage_prep();
else else
patch_rootdir(); patch_rootdir();
exec_init(); exec_init();
} }
}; };
/************ /************
@@ -140,23 +140,23 @@ public:
class RootFSInit : public MagiskInit { class RootFSInit : public MagiskInit {
private: private:
void early_mount(); void early_mount();
void patch_rootfs(); void patch_rootfs();
public: public:
RootFSInit(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) { RootFSInit(char *argv[], cmdline *cmd) : MagiskInit(argv, cmd) {
LOGD("%s\n", __FUNCTION__); LOGD("%s\n", __FUNCTION__);
} }
void start() override { void start() override {
early_mount(); early_mount();
patch_rootfs(); patch_rootfs();
exec_init(); exec_init();
} }
}; };
class MagiskProxy : public MagiskInit { class MagiskProxy : public MagiskInit {
public: public:
explicit MagiskProxy(char *argv[]) : MagiskInit(argv, nullptr) { explicit MagiskProxy(char *argv[]) : MagiskInit(argv, nullptr) {
LOGD("%s\n", __FUNCTION__); LOGD("%s\n", __FUNCTION__);
} }
void start() override; void start() override;
}; };

View File

@@ -11,406 +11,406 @@
using namespace std; using namespace std;
static string rtrim(string &&str) { static string rtrim(string &&str) {
// Trim space, newline, and null byte from end of string // Trim space, newline, and null byte from end of string
while (memchr(" \n\r", str[str.length() - 1], 4)) while (memchr(" \n\r", str[str.length() - 1], 4))
str.pop_back(); str.pop_back();
return std::move(str); return std::move(str);
} }
struct devinfo { struct devinfo {
int major; int major;
int minor; int minor;
char devname[32]; char devname[32];
char partname[32]; char partname[32];
char dmname[32]; char dmname[32];
}; };
static vector<devinfo> dev_list; static vector<devinfo> dev_list;
static void parse_device(devinfo *dev, const char *uevent) { static void parse_device(devinfo *dev, const char *uevent) {
dev->partname[0] = '\0'; dev->partname[0] = '\0';
parse_prop_file(uevent, [=](string_view key, string_view value) -> bool { parse_prop_file(uevent, [=](string_view key, string_view value) -> bool {
if (key == "MAJOR") if (key == "MAJOR")
dev->major = parse_int(value.data()); dev->major = parse_int(value.data());
else if (key == "MINOR") else if (key == "MINOR")
dev->minor = parse_int(value.data()); dev->minor = parse_int(value.data());
else if (key == "DEVNAME") else if (key == "DEVNAME")
strcpy(dev->devname, value.data()); strcpy(dev->devname, value.data());
else if (key == "PARTNAME") else if (key == "PARTNAME")
strcpy(dev->partname, value.data()); strcpy(dev->partname, value.data());
return true; return true;
}); });
} }
static void collect_devices() { static void collect_devices() {
char path[128]; char path[128];
devinfo dev{}; devinfo dev{};
if (auto dir = xopen_dir("/sys/dev/block"); dir) { if (auto dir = xopen_dir("/sys/dev/block"); dir) {
for (dirent *entry; (entry = readdir(dir.get()));) { for (dirent *entry; (entry = readdir(dir.get()));) {
if (entry->d_name == "."sv || entry->d_name == ".."sv) if (entry->d_name == "."sv || entry->d_name == ".."sv)
continue; continue;
sprintf(path, "/sys/dev/block/%s/uevent", entry->d_name); sprintf(path, "/sys/dev/block/%s/uevent", entry->d_name);
parse_device(&dev, path); parse_device(&dev, path);
sprintf(path, "/sys/dev/block/%s/dm/name", entry->d_name); sprintf(path, "/sys/dev/block/%s/dm/name", entry->d_name);
if (access(path, F_OK) == 0) { if (access(path, F_OK) == 0) {
auto name = rtrim(full_read(path)); auto name = rtrim(full_read(path));
strcpy(dev.dmname, name.data()); strcpy(dev.dmname, name.data());
} }
dev_list.push_back(dev); dev_list.push_back(dev);
} }
} }
} }
static struct { static struct {
char partname[32]; char partname[32];
char block_dev[64]; char block_dev[64];
} blk_info; } blk_info;
static int64_t setup_block(bool write_block) { static int64_t setup_block(bool write_block) {
if (dev_list.empty()) if (dev_list.empty())
collect_devices(); collect_devices();
xmkdir("/dev", 0755); xmkdir("/dev", 0755);
xmkdir("/dev/block", 0755); xmkdir("/dev/block", 0755);
for (int tries = 0; tries < 3; ++tries) { for (int tries = 0; tries < 3; ++tries) {
for (auto &dev : dev_list) { for (auto &dev : dev_list) {
if (strcasecmp(dev.partname, blk_info.partname) == 0) if (strcasecmp(dev.partname, blk_info.partname) == 0)
LOGD("Setup %s: [%s] (%d, %d)\n", dev.partname, dev.devname, dev.major, dev.minor); LOGD("Setup %s: [%s] (%d, %d)\n", dev.partname, dev.devname, dev.major, dev.minor);
else if (strcasecmp(dev.dmname, blk_info.partname) == 0) else if (strcasecmp(dev.dmname, blk_info.partname) == 0)
LOGD("Setup %s: [%s] (%d, %d)\n", dev.dmname, dev.devname, dev.major, dev.minor); LOGD("Setup %s: [%s] (%d, %d)\n", dev.dmname, dev.devname, dev.major, dev.minor);
else else
continue; continue;
if (write_block) { if (write_block) {
sprintf(blk_info.block_dev, "/dev/block/%s", dev.devname); sprintf(blk_info.block_dev, "/dev/block/%s", dev.devname);
} }
dev_t rdev = makedev(dev.major, dev.minor); dev_t rdev = makedev(dev.major, dev.minor);
mknod(blk_info.block_dev, S_IFBLK | 0600, rdev); mknod(blk_info.block_dev, S_IFBLK | 0600, rdev);
return rdev; return rdev;
} }
// Wait 10ms and try again // Wait 10ms and try again
usleep(10000); usleep(10000);
dev_list.clear(); dev_list.clear();
collect_devices(); collect_devices();
} }
// The requested partname does not exist // The requested partname does not exist
return -1; return -1;
} }
static bool is_lnk(const char *name) { static bool is_lnk(const char *name) {
struct stat st; struct stat st;
if (lstat(name, &st)) if (lstat(name, &st))
return false; return false;
return S_ISLNK(st.st_mode); return S_ISLNK(st.st_mode);
} }
#define read_info(val) \ #define read_info(val) \
if (access(#val, F_OK) == 0) {\ if (access(#val, F_OK) == 0) {\
entry.val = rtrim(full_read(#val)); \ entry.val = rtrim(full_read(#val)); \
} }
void BaseInit::read_dt_fstab(vector<fstab_entry> &fstab) { void BaseInit::read_dt_fstab(vector<fstab_entry> &fstab) {
if (access(cmd->dt_dir, F_OK) != 0) if (access(cmd->dt_dir, F_OK) != 0)
return; return;
char cwd[128]; char cwd[128];
getcwd(cwd, sizeof(cwd)); getcwd(cwd, sizeof(cwd));
chdir(cmd->dt_dir); chdir(cmd->dt_dir);
run_finally cd([&]{ chdir(cwd); }); run_finally cd([&]{ chdir(cwd); });
if (access("fstab", F_OK) != 0) if (access("fstab", F_OK) != 0)
return; return;
chdir("fstab"); chdir("fstab");
// Make sure dt fstab is enabled // Make sure dt fstab is enabled
if (access("status", F_OK) == 0) { if (access("status", F_OK) == 0) {
auto status = rtrim(full_read("status")); auto status = rtrim(full_read("status"));
if (status != "okay" && status != "ok") if (status != "okay" && status != "ok")
return; return;
} }
auto dir = xopen_dir("."); auto dir = xopen_dir(".");
for (dirent *dp; (dp = xreaddir(dir.get()));) { for (dirent *dp; (dp = xreaddir(dir.get()));) {
if (dp->d_type != DT_DIR) if (dp->d_type != DT_DIR)
continue; continue;
chdir(dp->d_name); chdir(dp->d_name);
run_finally f([]{ chdir(".."); }); run_finally f([]{ chdir(".."); });
if (access("status", F_OK) == 0) { if (access("status", F_OK) == 0) {
auto status = rtrim(full_read("status")); auto status = rtrim(full_read("status"));
if (status != "okay" && status != "ok") if (status != "okay" && status != "ok")
continue; continue;
} }
fstab_entry entry; fstab_entry entry;
read_info(dev); read_info(dev);
read_info(mnt_point) else { read_info(mnt_point) else {
entry.mnt_point = "/"; entry.mnt_point = "/";
entry.mnt_point += dp->d_name; entry.mnt_point += dp->d_name;
} }
read_info(type); read_info(type);
read_info(mnt_flags); read_info(mnt_flags);
read_info(fsmgr_flags); read_info(fsmgr_flags);
fstab.emplace_back(std::move(entry)); fstab.emplace_back(std::move(entry));
} }
} }
void MagiskInit::mount_with_dt() { void MagiskInit::mount_with_dt() {
vector<fstab_entry> fstab; vector<fstab_entry> fstab;
read_dt_fstab(fstab); read_dt_fstab(fstab);
for (const auto &entry : fstab) { for (const auto &entry : fstab) {
if (is_lnk(entry.mnt_point.data())) if (is_lnk(entry.mnt_point.data()))
continue; continue;
// Derive partname from dev // Derive partname from dev
sprintf(blk_info.partname, "%s%s", basename(entry.dev.data()), cmd->slot); sprintf(blk_info.partname, "%s%s", basename(entry.dev.data()), cmd->slot);
setup_block(true); setup_block(true);
xmkdir(entry.mnt_point.data(), 0755); xmkdir(entry.mnt_point.data(), 0755);
xmount(blk_info.block_dev, entry.mnt_point.data(), entry.type.data(), MS_RDONLY, nullptr); xmount(blk_info.block_dev, entry.mnt_point.data(), entry.type.data(), MS_RDONLY, nullptr);
mount_list.push_back(entry.mnt_point); mount_list.push_back(entry.mnt_point);
} }
} }
static void switch_root(const string &path) { static void switch_root(const string &path) {
LOGD("Switch root to %s\n", path.data()); LOGD("Switch root to %s\n", path.data());
int root = xopen("/", O_RDONLY); int root = xopen("/", O_RDONLY);
vector<string> mounts; vector<string> mounts;
parse_mnt("/proc/mounts", [&](mntent *me) { parse_mnt("/proc/mounts", [&](mntent *me) {
// Skip root and self // Skip root and self
if (me->mnt_dir == "/"sv || me->mnt_dir == path) if (me->mnt_dir == "/"sv || me->mnt_dir == path)
return true; return true;
// Do not include subtrees // Do not include subtrees
for (const auto &m : mounts) { for (const auto &m : mounts) {
if (strncmp(me->mnt_dir, m.data(), m.length()) == 0 && me->mnt_dir[m.length()] == '/') if (strncmp(me->mnt_dir, m.data(), m.length()) == 0 && me->mnt_dir[m.length()] == '/')
return true; return true;
} }
mounts.emplace_back(me->mnt_dir); mounts.emplace_back(me->mnt_dir);
return true; return true;
}); });
for (auto &dir : mounts) { for (auto &dir : mounts) {
auto new_path = path + dir; auto new_path = path + dir;
mkdir(new_path.data(), 0755); mkdir(new_path.data(), 0755);
xmount(dir.data(), new_path.data(), nullptr, MS_MOVE, nullptr); xmount(dir.data(), new_path.data(), nullptr, MS_MOVE, nullptr);
} }
chdir(path.data()); chdir(path.data());
xmount(path.data(), "/", nullptr, MS_MOVE, nullptr); xmount(path.data(), "/", nullptr, MS_MOVE, nullptr);
chroot("."); chroot(".");
LOGD("Cleaning rootfs\n"); LOGD("Cleaning rootfs\n");
frm_rf(root); frm_rf(root);
} }
void MagiskInit::mount_rules_dir(const char *dev_base, const char *mnt_base) { void MagiskInit::mount_rules_dir(const char *dev_base, const char *mnt_base) {
char path[128]; char path[128];
xrealpath(dev_base, blk_info.block_dev); xrealpath(dev_base, blk_info.block_dev);
xrealpath(mnt_base, path); xrealpath(mnt_base, path);
char *b = blk_info.block_dev + strlen(blk_info.block_dev); char *b = blk_info.block_dev + strlen(blk_info.block_dev);
char *p = path + strlen(path); char *p = path + strlen(path);
auto do_mount = [&](const char *type) -> bool { auto do_mount = [&](const char *type) -> bool {
xmkdir(path, 0755); xmkdir(path, 0755);
bool success = xmount(blk_info.block_dev, path, type, 0, nullptr) == 0; bool success = xmount(blk_info.block_dev, path, type, 0, nullptr) == 0;
if (success) if (success)
mount_list.emplace_back(path); mount_list.emplace_back(path);
return success; return success;
}; };
// First try userdata // First try userdata
strcpy(blk_info.partname, "userdata"); strcpy(blk_info.partname, "userdata");
strcpy(b, "/data"); strcpy(b, "/data");
strcpy(p, "/data"); strcpy(p, "/data");
if (setup_block(false) < 0) { if (setup_block(false) < 0) {
// Try NVIDIA naming scheme // Try NVIDIA naming scheme
strcpy(blk_info.partname, "UDA"); strcpy(blk_info.partname, "UDA");
if (setup_block(false) < 0) if (setup_block(false) < 0)
goto cache; goto cache;
} }
// Try to mount with either ext4 or f2fs // Try to mount with either ext4 or f2fs
// Failure means either FDE or metadata encryption // Failure means either FDE or metadata encryption
if (!do_mount("ext4") && !do_mount("f2fs")) if (!do_mount("ext4") && !do_mount("f2fs"))
goto cache; goto cache;
strcpy(p, "/data/unencrypted"); strcpy(p, "/data/unencrypted");
if (access(path, F_OK) == 0) { if (access(path, F_OK) == 0) {
// FBE, need to use an unencrypted path // FBE, need to use an unencrypted path
custom_rules_dir = path + "/magisk"s; custom_rules_dir = path + "/magisk"s;
} else { } else {
// Skip if /data/adb does not exist // Skip if /data/adb does not exist
strcpy(p, "/data/adb"); strcpy(p, "/data/adb");
if (access(path, F_OK) != 0) if (access(path, F_OK) != 0)
return; return;
// Unencrypted, directly use module paths // Unencrypted, directly use module paths
custom_rules_dir = string(mnt_base) + MODULEROOT; custom_rules_dir = string(mnt_base) + MODULEROOT;
} }
goto success; goto success;
cache: cache:
// Fallback to cache // Fallback to cache
strcpy(blk_info.partname, "cache"); strcpy(blk_info.partname, "cache");
strcpy(b, "/cache"); strcpy(b, "/cache");
strcpy(p, "/cache"); strcpy(p, "/cache");
if (setup_block(false) < 0) { if (setup_block(false) < 0) {
// Try NVIDIA naming scheme // Try NVIDIA naming scheme
strcpy(blk_info.partname, "CAC"); strcpy(blk_info.partname, "CAC");
if (setup_block(false) < 0) if (setup_block(false) < 0)
goto metadata; goto metadata;
} }
if (!do_mount("ext4")) if (!do_mount("ext4"))
goto metadata; goto metadata;
custom_rules_dir = path + "/magisk"s; custom_rules_dir = path + "/magisk"s;
goto success; goto success;
metadata: metadata:
// Fallback to metadata // Fallback to metadata
strcpy(blk_info.partname, "metadata"); strcpy(blk_info.partname, "metadata");
strcpy(b, "/metadata"); strcpy(b, "/metadata");
strcpy(p, "/metadata"); strcpy(p, "/metadata");
if (setup_block(false) < 0 || !do_mount("ext4")) if (setup_block(false) < 0 || !do_mount("ext4"))
goto persist; goto persist;
custom_rules_dir = path + "/magisk"s; custom_rules_dir = path + "/magisk"s;
goto success; goto success;
persist: persist:
// Fallback to persist // Fallback to persist
strcpy(blk_info.partname, "persist"); strcpy(blk_info.partname, "persist");
strcpy(b, "/persist"); strcpy(b, "/persist");
strcpy(p, "/persist"); strcpy(p, "/persist");
if (setup_block(false) < 0 || !do_mount("ext4")) if (setup_block(false) < 0 || !do_mount("ext4"))
return; return;
custom_rules_dir = path + "/magisk"s; custom_rules_dir = path + "/magisk"s;
success: success:
// Create symlinks so we don't need to go through this logic again // Create symlinks so we don't need to go through this logic again
strcpy(p, "/sepolicy.rules"); strcpy(p, "/sepolicy.rules");
xsymlink(custom_rules_dir.data(), path); xsymlink(custom_rules_dir.data(), path);
} }
void RootFSInit::early_mount() { void RootFSInit::early_mount() {
self = raw_data::read("/init"); self = raw_data::read("/init");
LOGD("Restoring /init\n"); LOGD("Restoring /init\n");
rename("/.backup/init", "/init"); rename("/.backup/init", "/init");
mount_with_dt(); mount_with_dt();
} }
void SARBase::backup_files() { void SARBase::backup_files() {
if (access("/overlay.d", F_OK) == 0) if (access("/overlay.d", F_OK) == 0)
backup_folder("/overlay.d", overlays); backup_folder("/overlay.d", overlays);
self = raw_data::read("/proc/self/exe"); self = raw_data::read("/proc/self/exe");
if (access("/.backup/.magisk", R_OK) == 0) if (access("/.backup/.magisk", R_OK) == 0)
config = raw_data::read("/.backup/.magisk"); config = raw_data::read("/.backup/.magisk");
} }
void SARBase::mount_system_root() { void SARBase::mount_system_root() {
LOGD("Early mount system_root\n"); LOGD("Early mount system_root\n");
strcpy(blk_info.block_dev, "/dev/root"); strcpy(blk_info.block_dev, "/dev/root");
do { do {
// Try legacy SAR dm-verity // Try legacy SAR dm-verity
strcpy(blk_info.partname, "vroot"); strcpy(blk_info.partname, "vroot");
auto dev = setup_block(false); auto dev = setup_block(false);
if (dev >= 0) if (dev >= 0)
goto mount_root; goto mount_root;
// Try NVIDIA naming scheme // Try NVIDIA naming scheme
strcpy(blk_info.partname, "APP"); strcpy(blk_info.partname, "APP");
dev = setup_block(false); dev = setup_block(false);
if (dev >= 0) if (dev >= 0)
goto mount_root; goto mount_root;
sprintf(blk_info.partname, "system%s", cmd->slot); sprintf(blk_info.partname, "system%s", cmd->slot);
dev = setup_block(false); dev = setup_block(false);
if (dev >= 0) if (dev >= 0)
goto mount_root; goto mount_root;
// Poll forever if rootwait was given in cmdline // Poll forever if rootwait was given in cmdline
} while (cmd->rootwait); } while (cmd->rootwait);
// We don't really know what to do at this point... // We don't really know what to do at this point...
LOGE("Cannot find root partition, abort\n"); LOGE("Cannot find root partition, abort\n");
exit(1); exit(1);
mount_root: mount_root:
xmkdir("/system_root", 0755); xmkdir("/system_root", 0755);
if (xmount("/dev/root", "/system_root", "ext4", MS_RDONLY, nullptr)) if (xmount("/dev/root", "/system_root", "ext4", MS_RDONLY, nullptr))
xmount("/dev/root", "/system_root", "erofs", MS_RDONLY, nullptr); xmount("/dev/root", "/system_root", "erofs", MS_RDONLY, nullptr);
} }
void SARInit::early_mount() { void SARInit::early_mount() {
backup_files(); backup_files();
mount_system_root(); mount_system_root();
switch_root("/system_root"); switch_root("/system_root");
{ {
auto init = raw_data::mmap_ro("/init"); auto init = raw_data::mmap_ro("/init");
is_two_stage = init.contains("selinux_setup"); is_two_stage = init.contains("selinux_setup");
} }
LOGD("is_two_stage: [%d]\n", is_two_stage); LOGD("is_two_stage: [%d]\n", is_two_stage);
if (!is_two_stage) { if (!is_two_stage) {
// Make dev writable // Make dev writable
xmkdir("/dev", 0755); xmkdir("/dev", 0755);
xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755"); xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755");
mount_list.emplace_back("/dev"); mount_list.emplace_back("/dev");
mount_with_dt(); mount_with_dt();
} }
} }
void SecondStageInit::prepare() { void SecondStageInit::prepare() {
backup_files(); backup_files();
umount2("/init", MNT_DETACH); umount2("/init", MNT_DETACH);
umount2("/proc/self/exe", MNT_DETACH); umount2("/proc/self/exe", MNT_DETACH);
if (access("/system_root", F_OK) == 0) if (access("/system_root", F_OK) == 0)
switch_root("/system_root"); switch_root("/system_root");
} }
void BaseInit::exec_init() { void BaseInit::exec_init() {
// Unmount in reverse order // Unmount in reverse order
for (auto &p : reversed(mount_list)) { for (auto &p : reversed(mount_list)) {
if (xumount(p.data()) == 0) if (xumount(p.data()) == 0)
LOGD("Unmount [%s]\n", p.data()); LOGD("Unmount [%s]\n", p.data());
} }
execv("/init", argv); execv("/init", argv);
exit(1); exit(1);
} }
static void patch_socket_name(const char *path) { static void patch_socket_name(const char *path) {
char rstr[16]; char rstr[16];
gen_rand_str(rstr, sizeof(rstr)); gen_rand_str(rstr, sizeof(rstr));
auto bin = raw_data::mmap_rw(path); auto bin = raw_data::mmap_rw(path);
bin.patch({ make_pair(MAIN_SOCKET, rstr) }); bin.patch({ make_pair(MAIN_SOCKET, rstr) });
} }
void MagiskInit::setup_tmp(const char *path) { void MagiskInit::setup_tmp(const char *path) {
LOGD("Setup Magisk tmp at %s\n", path); LOGD("Setup Magisk tmp at %s\n", path);
xmount("tmpfs", path, "tmpfs", 0, "mode=755"); xmount("tmpfs", path, "tmpfs", 0, "mode=755");
chdir(path); chdir(path);
xmkdir(INTLROOT, 0755); xmkdir(INTLROOT, 0755);
xmkdir(MIRRDIR, 0); xmkdir(MIRRDIR, 0);
xmkdir(BLOCKDIR, 0); xmkdir(BLOCKDIR, 0);
int fd = xopen(INTLROOT "/config", O_WRONLY | O_CREAT, 0); int fd = xopen(INTLROOT "/config", O_WRONLY | O_CREAT, 0);
xwrite(fd, config.buf, config.sz); xwrite(fd, config.buf, config.sz);
close(fd); close(fd);
fd = xopen("magiskinit", O_WRONLY | O_CREAT, 0755); fd = xopen("magiskinit", O_WRONLY | O_CREAT, 0755);
xwrite(fd, self.buf, self.sz); xwrite(fd, self.buf, self.sz);
close(fd); close(fd);
dump_magisk("magisk", 0755); dump_magisk("magisk", 0755);
patch_socket_name("magisk"); patch_socket_name("magisk");
// Create applet symlinks // Create applet symlinks
for (int i = 0; applet_names[i]; ++i) for (int i = 0; applet_names[i]; ++i)
xsymlink("./magisk", applet_names[i]); xsymlink("./magisk", applet_names[i]);
xsymlink("./magiskinit", "magiskpolicy"); xsymlink("./magiskinit", "magiskpolicy");
xsymlink("./magiskinit", "supolicy"); xsymlink("./magiskinit", "supolicy");
chdir("/"); chdir("/");
} }

View File

@@ -3,62 +3,62 @@
using namespace std; using namespace std;
int data_holder::patch(str_pairs list) { int data_holder::patch(str_pairs list) {
if (buf == nullptr) if (buf == nullptr)
return 0; return 0;
int count = 0; int count = 0;
for (uint8_t *p = buf, *eof = buf + sz; p < eof; ++p) { for (uint8_t *p = buf, *eof = buf + sz; p < eof; ++p) {
for (auto [from, to] : list) { for (auto [from, to] : list) {
if (memcmp(p, from.data(), from.length() + 1) == 0) { if (memcmp(p, from.data(), from.length() + 1) == 0) {
LOGD("Replace [%s] -> [%s]\n", from.data(), to.data()); LOGD("Replace [%s] -> [%s]\n", from.data(), to.data());
memset(p, 0, from.length()); memset(p, 0, from.length());
memcpy(p, to.data(), to.length()); memcpy(p, to.data(), to.length());
++count; ++count;
p += from.length(); p += from.length();
} }
} }
} }
return count; return count;
} }
bool data_holder::contains(string_view pattern) { bool data_holder::contains(string_view pattern) {
if (buf == nullptr) if (buf == nullptr)
return false; return false;
for (uint8_t *p = buf, *eof = buf + sz; p < eof; ++p) { for (uint8_t *p = buf, *eof = buf + sz; p < eof; ++p) {
if (memcmp(p, pattern.data(), pattern.length() + 1) == 0) { if (memcmp(p, pattern.data(), pattern.length() + 1) == 0) {
LOGD("Found pattern [%s]\n", pattern.data()); LOGD("Found pattern [%s]\n", pattern.data());
return true; return true;
} }
} }
return false; return false;
} }
void data_holder::consume(data_holder &other) { void data_holder::consume(data_holder &other) {
buf = other.buf; buf = other.buf;
sz = other.sz; sz = other.sz;
other.buf = nullptr; other.buf = nullptr;
other.sz = 0; other.sz = 0;
} }
auto_data<HEAP> raw_data::read(int fd) { auto_data<HEAP> raw_data::read(int fd) {
auto_data<HEAP> data; auto_data<HEAP> data;
fd_full_read(fd, data.buf, data.sz); fd_full_read(fd, data.buf, data.sz);
return data; return data;
} }
auto_data<HEAP> raw_data::read(const char *name) { auto_data<HEAP> raw_data::read(const char *name) {
auto_data<HEAP> data; auto_data<HEAP> data;
full_read(name, data.buf, data.sz); full_read(name, data.buf, data.sz);
return data; return data;
} }
auto_data<MMAP> raw_data::mmap_rw(const char *name) { auto_data<MMAP> raw_data::mmap_rw(const char *name) {
auto_data<MMAP> data; auto_data<MMAP> data;
::mmap_rw(name, data.buf, data.sz); ::mmap_rw(name, data.buf, data.sz);
return data; return data;
} }
auto_data<MMAP> raw_data::mmap_ro(const char *name) { auto_data<MMAP> raw_data::mmap_ro(const char *name) {
auto_data<MMAP> data; auto_data<MMAP> data;
::mmap_ro(name, data.buf, data.sz); ::mmap_ro(name, data.buf, data.sz);
return data; return data;
} }

View File

@@ -3,30 +3,30 @@
#include <utils.hpp> #include <utils.hpp>
struct data_holder { struct data_holder {
uint8_t *buf = nullptr; uint8_t *buf = nullptr;
size_t sz = 0; size_t sz = 0;
using str_pairs = std::initializer_list<std::pair<std::string_view, std::string_view>>; using str_pairs = std::initializer_list<std::pair<std::string_view, std::string_view>>;
int patch(str_pairs list); int patch(str_pairs list);
bool contains(std::string_view pattern); bool contains(std::string_view pattern);
protected: protected:
void consume(data_holder &other); void consume(data_holder &other);
}; };
enum data_type { HEAP, MMAP }; enum data_type { HEAP, MMAP };
template <data_type T> template <data_type T>
struct auto_data : public data_holder { struct auto_data : public data_holder {
auto_data<T>() = default; auto_data<T>() = default;
auto_data<T>(const auto_data&) = delete; auto_data<T>(const auto_data&) = delete;
auto_data<T>(auto_data<T> &&other) { consume(other); } auto_data<T>(auto_data<T> &&other) { consume(other); }
~auto_data<T>() {} ~auto_data<T>() {}
auto_data<T>& operator=(auto_data<T> &&other) { consume(other); return *this; } auto_data<T>& operator=(auto_data<T> &&other) { consume(other); return *this; }
}; };
template <> inline auto_data<MMAP>::~auto_data<MMAP>() { if (buf) munmap(buf, sz); } template <> inline auto_data<MMAP>::~auto_data<MMAP>() { if (buf) munmap(buf, sz); }
template <> inline auto_data<HEAP>::~auto_data<HEAP>() { free(buf); } template <> inline auto_data<HEAP>::~auto_data<HEAP>() { free(buf); }
namespace raw_data { namespace raw_data {
auto_data<HEAP> read(const char *name); auto_data<HEAP> read(const char *name);
auto_data<HEAP> read(int fd); auto_data<HEAP> read(int fd);
auto_data<MMAP> mmap_rw(const char *name); auto_data<MMAP> mmap_rw(const char *name);
auto_data<MMAP> mmap_ro(const char *name); auto_data<MMAP> mmap_ro(const char *name);
} }

View File

@@ -20,164 +20,164 @@ using namespace std;
static vector<string> rc_list; static vector<string> rc_list;
static void patch_init_rc(const char *src, const char *dest, const char *tmp_dir) { static void patch_init_rc(const char *src, const char *dest, const char *tmp_dir) {
FILE *rc = xfopen(dest, "we"); FILE *rc = xfopen(dest, "we");
file_readline(src, [=](string_view line) -> bool { file_readline(src, [=](string_view line) -> bool {
// Do not start vaultkeeper // Do not start vaultkeeper
if (str_contains(line, "start vaultkeeper")) { if (str_contains(line, "start vaultkeeper")) {
LOGD("Remove vaultkeeper\n"); LOGD("Remove vaultkeeper\n");
return true; return true;
} }
// Do not run flash_recovery // Do not run flash_recovery
if (str_starts(line, "service flash_recovery")) { if (str_starts(line, "service flash_recovery")) {
LOGD("Remove flash_recovery\n"); LOGD("Remove flash_recovery\n");
fprintf(rc, "service flash_recovery /system/bin/xxxxx\n"); fprintf(rc, "service flash_recovery /system/bin/xxxxx\n");
return true; return true;
} }
// Else just write the line // Else just write the line
fprintf(rc, "%s", line.data()); fprintf(rc, "%s", line.data());
return true; return true;
}); });
fprintf(rc, "\n"); fprintf(rc, "\n");
// Inject custom rc scripts // Inject custom rc scripts
for (auto &script : rc_list) { for (auto &script : rc_list) {
// Replace template arguments of rc scripts with dynamic paths // Replace template arguments of rc scripts with dynamic paths
replace_all(script, "${MAGISKTMP}", tmp_dir); replace_all(script, "${MAGISKTMP}", tmp_dir);
fprintf(rc, "\n%s\n", script.data()); fprintf(rc, "\n%s\n", script.data());
} }
rc_list.clear(); rc_list.clear();
// Inject Magisk rc scripts // Inject Magisk rc scripts
char pfd_svc[16], ls_svc[16], bc_svc[16]; char pfd_svc[16], ls_svc[16], bc_svc[16];
gen_rand_str(pfd_svc, sizeof(pfd_svc)); gen_rand_str(pfd_svc, sizeof(pfd_svc));
gen_rand_str(ls_svc, sizeof(ls_svc)); gen_rand_str(ls_svc, sizeof(ls_svc));
gen_rand_str(bc_svc, sizeof(bc_svc)); gen_rand_str(bc_svc, sizeof(bc_svc));
LOGD("Inject magisk services: [%s] [%s] [%s]\n", pfd_svc, ls_svc, bc_svc); LOGD("Inject magisk services: [%s] [%s] [%s]\n", pfd_svc, ls_svc, bc_svc);
fprintf(rc, MAGISK_RC, tmp_dir, pfd_svc, ls_svc, bc_svc); fprintf(rc, MAGISK_RC, tmp_dir, pfd_svc, ls_svc, bc_svc);
fclose(rc); fclose(rc);
clone_attr(src, dest); clone_attr(src, dest);
} }
static void load_overlay_rc(const char *overlay) { static void load_overlay_rc(const char *overlay) {
auto dir = open_dir(overlay); auto dir = open_dir(overlay);
if (!dir) return; if (!dir) return;
int dfd = dirfd(dir.get()); int dfd = dirfd(dir.get());
// Do not allow overwrite init.rc // Do not allow overwrite init.rc
unlinkat(dfd, "init.rc", 0); unlinkat(dfd, "init.rc", 0);
for (dirent *entry; (entry = xreaddir(dir.get()));) { for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (str_ends(entry->d_name, ".rc")) { if (str_ends(entry->d_name, ".rc")) {
LOGD("Found rc script [%s]\n", entry->d_name); LOGD("Found rc script [%s]\n", entry->d_name);
int rc = xopenat(dfd, entry->d_name, O_RDONLY | O_CLOEXEC); int rc = xopenat(dfd, entry->d_name, O_RDONLY | O_CLOEXEC);
rc_list.push_back(fd_full_read(rc)); rc_list.push_back(fd_full_read(rc));
close(rc); close(rc);
unlinkat(dfd, entry->d_name, 0); unlinkat(dfd, entry->d_name, 0);
} }
} }
} }
bool MagiskInit::patch_sepolicy(const char *file) { bool MagiskInit::patch_sepolicy(const char *file) {
bool patch_init = false; bool patch_init = false;
sepolicy *sepol = nullptr; sepolicy *sepol = nullptr;
if (access(SPLIT_PLAT_CIL, R_OK) == 0) { if (access(SPLIT_PLAT_CIL, R_OK) == 0) {
LOGD("sepol: split policy\n"); LOGD("sepol: split policy\n");
patch_init = true; patch_init = true;
} else if (access("/sepolicy", R_OK) == 0) { } else if (access("/sepolicy", R_OK) == 0) {
LOGD("sepol: monolithic policy\n"); LOGD("sepol: monolithic policy\n");
sepol = sepolicy::from_file("/sepolicy"); sepol = sepolicy::from_file("/sepolicy");
} else { } else {
LOGD("sepol: no selinux\n"); LOGD("sepol: no selinux\n");
return false; return false;
} }
if (access(SELINUX_VERSION, F_OK) != 0) { if (access(SELINUX_VERSION, F_OK) != 0) {
// Mount selinuxfs to communicate with kernel // Mount selinuxfs to communicate with kernel
xmount("selinuxfs", SELINUX_MNT, "selinuxfs", 0, nullptr); xmount("selinuxfs", SELINUX_MNT, "selinuxfs", 0, nullptr);
mount_list.emplace_back(SELINUX_MNT); mount_list.emplace_back(SELINUX_MNT);
} }
if (patch_init) if (patch_init)
sepol = sepolicy::from_split(); sepol = sepolicy::from_split();
sepol->magisk_rules(); sepol->magisk_rules();
// Custom rules // Custom rules
if (!custom_rules_dir.empty()) { if (!custom_rules_dir.empty()) {
if (auto dir = open_dir(custom_rules_dir.data())) { if (auto dir = open_dir(custom_rules_dir.data())) {
for (dirent *entry; (entry = xreaddir(dir.get()));) { for (dirent *entry; (entry = xreaddir(dir.get()));) {
auto rule = custom_rules_dir + "/" + entry->d_name + "/sepolicy.rule"; auto rule = custom_rules_dir + "/" + entry->d_name + "/sepolicy.rule";
if (access(rule.data(), R_OK) == 0) { if (access(rule.data(), R_OK) == 0) {
LOGD("Loading custom sepolicy patch: [%s]\n", rule.data()); LOGD("Loading custom sepolicy patch: [%s]\n", rule.data());
sepol->load_rule_file(rule.data()); sepol->load_rule_file(rule.data());
} }
} }
} }
} }
LOGD("Dumping sepolicy to: [%s]\n", file); LOGD("Dumping sepolicy to: [%s]\n", file);
sepol->to_file(file); sepol->to_file(file);
delete sepol; delete sepol;
// Remove OnePlus stupid debug sepolicy and use our own // Remove OnePlus stupid debug sepolicy and use our own
if (access("/sepolicy_debug", F_OK) == 0) { if (access("/sepolicy_debug", F_OK) == 0) {
unlink("/sepolicy_debug"); unlink("/sepolicy_debug");
link("/sepolicy", "/sepolicy_debug"); link("/sepolicy", "/sepolicy_debug");
} }
return patch_init; return patch_init;
} }
static void recreate_sbin(const char *mirror, bool use_bind_mount) { static void recreate_sbin(const char *mirror, bool use_bind_mount) {
auto dp = xopen_dir(mirror); auto dp = xopen_dir(mirror);
int src = dirfd(dp.get()); int src = dirfd(dp.get());
char buf[4096]; char buf[4096];
for (dirent *entry; (entry = xreaddir(dp.get()));) { for (dirent *entry; (entry = xreaddir(dp.get()));) {
string sbin_path = "/sbin/"s + entry->d_name; string sbin_path = "/sbin/"s + entry->d_name;
struct stat st; struct stat st;
fstatat(src, entry->d_name, &st, AT_SYMLINK_NOFOLLOW); fstatat(src, entry->d_name, &st, AT_SYMLINK_NOFOLLOW);
if (S_ISLNK(st.st_mode)) { if (S_ISLNK(st.st_mode)) {
xreadlinkat(src, entry->d_name, buf, sizeof(buf)); xreadlinkat(src, entry->d_name, buf, sizeof(buf));
xsymlink(buf, sbin_path.data()); xsymlink(buf, sbin_path.data());
} else { } else {
sprintf(buf, "%s/%s", mirror, entry->d_name); sprintf(buf, "%s/%s", mirror, entry->d_name);
if (use_bind_mount) { if (use_bind_mount) {
auto mode = st.st_mode & 0777; auto mode = st.st_mode & 0777;
// Create dummy // Create dummy
if (S_ISDIR(st.st_mode)) if (S_ISDIR(st.st_mode))
xmkdir(sbin_path.data(), mode); xmkdir(sbin_path.data(), mode);
else else
close(xopen(sbin_path.data(), O_CREAT | O_WRONLY | O_CLOEXEC, mode)); close(xopen(sbin_path.data(), O_CREAT | O_WRONLY | O_CLOEXEC, mode));
xmount(buf, sbin_path.data(), nullptr, MS_BIND, nullptr); xmount(buf, sbin_path.data(), nullptr, MS_BIND, nullptr);
} else { } else {
xsymlink(buf, sbin_path.data()); xsymlink(buf, sbin_path.data());
} }
} }
} }
} }
static string magic_mount_list; static string magic_mount_list;
static void magic_mount(const string &sdir, const string &ddir = "") { static void magic_mount(const string &sdir, const string &ddir = "") {
auto dir = xopen_dir(sdir.data()); auto dir = xopen_dir(sdir.data());
for (dirent *entry; (entry = xreaddir(dir.get()));) { for (dirent *entry; (entry = xreaddir(dir.get()));) {
string src = sdir + "/" + entry->d_name; string src = sdir + "/" + entry->d_name;
string dest = ddir + "/" + entry->d_name; string dest = ddir + "/" + entry->d_name;
if (access(dest.data(), F_OK) == 0) { if (access(dest.data(), F_OK) == 0) {
if (entry->d_type == DT_DIR) { if (entry->d_type == DT_DIR) {
// Recursive // Recursive
magic_mount(src, dest); magic_mount(src, dest);
} else { } else {
LOGD("Mount [%s] -> [%s]\n", src.data(), dest.data()); LOGD("Mount [%s] -> [%s]\n", src.data(), dest.data());
xmount(src.data(), dest.data(), nullptr, MS_BIND, nullptr); xmount(src.data(), dest.data(), nullptr, MS_BIND, nullptr);
magic_mount_list += dest; magic_mount_list += dest;
magic_mount_list += '\n'; magic_mount_list += '\n';
} }
} }
} }
} }
#define ROOTMIR MIRRDIR "/system_root" #define ROOTMIR MIRRDIR "/system_root"
@@ -186,169 +186,169 @@ static void magic_mount(const string &sdir, const string &ddir = "") {
#define NEW_INITRC "/system/etc/init/hw/init.rc" #define NEW_INITRC "/system/etc/init/hw/init.rc"
void SARBase::patch_rootdir() { void SARBase::patch_rootdir() {
string tmp_dir; string tmp_dir;
const char *sepol; const char *sepol;
if (access("/sbin", F_OK) == 0) { if (access("/sbin", F_OK) == 0) {
tmp_dir = "/sbin"; tmp_dir = "/sbin";
sepol = "/sbin/.se"; sepol = "/sbin/.se";
} else { } else {
char buf[8]; char buf[8];
gen_rand_str(buf, sizeof(buf)); gen_rand_str(buf, sizeof(buf));
tmp_dir = "/dev/"s + buf; tmp_dir = "/dev/"s + buf;
xmkdir(tmp_dir.data(), 0); xmkdir(tmp_dir.data(), 0);
sepol = "/dev/.se"; sepol = "/dev/.se";
} }
setup_tmp(tmp_dir.data()); setup_tmp(tmp_dir.data());
chdir(tmp_dir.data()); chdir(tmp_dir.data());
mount_rules_dir(BLOCKDIR, MIRRDIR); mount_rules_dir(BLOCKDIR, MIRRDIR);
// Mount system_root mirror // Mount system_root mirror
xmkdir(ROOTMIR, 0755); xmkdir(ROOTMIR, 0755);
xmount("/", ROOTMIR, nullptr, MS_BIND, nullptr); xmount("/", ROOTMIR, nullptr, MS_BIND, nullptr);
mount_list.emplace_back(tmp_dir + "/" ROOTMIR); mount_list.emplace_back(tmp_dir + "/" ROOTMIR);
// Recreate original sbin structure if necessary // Recreate original sbin structure if necessary
if (tmp_dir == "/sbin") if (tmp_dir == "/sbin")
recreate_sbin(ROOTMIR "/sbin", true); recreate_sbin(ROOTMIR "/sbin", true);
// Patch init // Patch init
int patch_count; int patch_count;
{ {
int src = xopen("/init", O_RDONLY | O_CLOEXEC); int src = xopen("/init", O_RDONLY | O_CLOEXEC);
auto init = raw_data::read(src); auto init = raw_data::read(src);
patch_count = init.patch({ patch_count = init.patch({
make_pair(SPLIT_PLAT_CIL, "xxx"), /* Force loading monolithic sepolicy */ make_pair(SPLIT_PLAT_CIL, "xxx"), /* Force loading monolithic sepolicy */
make_pair(MONOPOLICY, sepol) /* Redirect /sepolicy to custom path */ make_pair(MONOPOLICY, sepol) /* Redirect /sepolicy to custom path */
}); });
xmkdir(ROOTOVL, 0); xmkdir(ROOTOVL, 0);
int dest = xopen(ROOTOVL "/init", O_CREAT | O_WRONLY | O_CLOEXEC, 0); int dest = xopen(ROOTOVL "/init", O_CREAT | O_WRONLY | O_CLOEXEC, 0);
xwrite(dest, init.buf, init.sz); xwrite(dest, init.buf, init.sz);
fclone_attr(src, dest); fclone_attr(src, dest);
close(src); close(src);
close(dest); close(dest);
} }
if (patch_count != 2 && access(LIBSELINUX, F_OK) == 0) { if (patch_count != 2 && access(LIBSELINUX, F_OK) == 0) {
// init is dynamically linked, need to patch libselinux // init is dynamically linked, need to patch libselinux
auto lib = raw_data::read(LIBSELINUX); auto lib = raw_data::read(LIBSELINUX);
lib.patch({make_pair(MONOPOLICY, sepol)}); lib.patch({make_pair(MONOPOLICY, sepol)});
xmkdirs(dirname(ROOTOVL LIBSELINUX), 0755); xmkdirs(dirname(ROOTOVL LIBSELINUX), 0755);
int dest = xopen(ROOTOVL LIBSELINUX, O_CREAT | O_WRONLY | O_CLOEXEC, 0); int dest = xopen(ROOTOVL LIBSELINUX, O_CREAT | O_WRONLY | O_CLOEXEC, 0);
xwrite(dest, lib.buf, lib.sz); xwrite(dest, lib.buf, lib.sz);
close(dest); close(dest);
clone_attr(LIBSELINUX, ROOTOVL LIBSELINUX); clone_attr(LIBSELINUX, ROOTOVL LIBSELINUX);
} }
// sepolicy // sepolicy
patch_sepolicy(sepol); patch_sepolicy(sepol);
// Restore backup files // Restore backup files
struct sockaddr_un sun; struct sockaddr_un sun;
int sockfd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); int sockfd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (connect(sockfd, (struct sockaddr*) &sun, setup_sockaddr(&sun, INIT_SOCKET)) == 0) { if (connect(sockfd, (struct sockaddr*) &sun, setup_sockaddr(&sun, INIT_SOCKET)) == 0) {
LOGD("ACK init daemon to write backup files\n"); LOGD("ACK init daemon to write backup files\n");
// Let daemon know where tmp_dir is // Let daemon know where tmp_dir is
write_string(sockfd, tmp_dir.data()); write_string(sockfd, tmp_dir.data());
// Wait for daemon to finish restoring files // Wait for daemon to finish restoring files
int ack; int ack;
read(sockfd, &ack, sizeof(ack)); read(sockfd, &ack, sizeof(ack));
} else { } else {
LOGD("Restore backup files locally\n"); LOGD("Restore backup files locally\n");
restore_folder(ROOTOVL, overlays); restore_folder(ROOTOVL, overlays);
overlays.clear(); overlays.clear();
} }
close(sockfd); close(sockfd);
// Handle overlay.d // Handle overlay.d
load_overlay_rc(ROOTOVL); load_overlay_rc(ROOTOVL);
if (access(ROOTOVL "/sbin", F_OK) == 0) { if (access(ROOTOVL "/sbin", F_OK) == 0) {
// Move files in overlay.d/sbin into tmp_dir // Move files in overlay.d/sbin into tmp_dir
mv_path(ROOTOVL "/sbin", "."); mv_path(ROOTOVL "/sbin", ".");
} }
// Patch init.rc // Patch init.rc
if (access("/init.rc", F_OK) == 0) { if (access("/init.rc", F_OK) == 0) {
patch_init_rc("/init.rc", ROOTOVL "/init.rc", tmp_dir.data()); patch_init_rc("/init.rc", ROOTOVL "/init.rc", tmp_dir.data());
} else { } else {
// Android 11's new init.rc // Android 11's new init.rc
xmkdirs(dirname(ROOTOVL NEW_INITRC), 0755); xmkdirs(dirname(ROOTOVL NEW_INITRC), 0755);
patch_init_rc(NEW_INITRC, ROOTOVL NEW_INITRC, tmp_dir.data()); patch_init_rc(NEW_INITRC, ROOTOVL NEW_INITRC, tmp_dir.data());
} }
// Mount rootdir // Mount rootdir
magic_mount(ROOTOVL); magic_mount(ROOTOVL);
int dest = xopen(ROOTMNT, O_WRONLY | O_CREAT | O_CLOEXEC, 0); int dest = xopen(ROOTMNT, O_WRONLY | O_CREAT | O_CLOEXEC, 0);
write(dest, magic_mount_list.data(), magic_mount_list.length()); write(dest, magic_mount_list.data(), magic_mount_list.length());
close(dest); close(dest);
chdir("/"); chdir("/");
} }
#define TMP_MNTDIR "/dev/mnt" #define TMP_MNTDIR "/dev/mnt"
#define TMP_RULESDIR "/.backup/.sepolicy.rules" #define TMP_RULESDIR "/.backup/.sepolicy.rules"
void RootFSInit::patch_rootfs() { void RootFSInit::patch_rootfs() {
// Handle custom sepolicy rules // Handle custom sepolicy rules
xmkdir(TMP_MNTDIR, 0755); xmkdir(TMP_MNTDIR, 0755);
mount_rules_dir("/dev/block", TMP_MNTDIR); mount_rules_dir("/dev/block", TMP_MNTDIR);
// Preserve custom rule path // Preserve custom rule path
if (!custom_rules_dir.empty()) { if (!custom_rules_dir.empty()) {
string rules_dir = "./" + custom_rules_dir.substr(sizeof(TMP_MNTDIR)); string rules_dir = "./" + custom_rules_dir.substr(sizeof(TMP_MNTDIR));
xsymlink(rules_dir.data(), TMP_RULESDIR); xsymlink(rules_dir.data(), TMP_RULESDIR);
} }
if (patch_sepolicy("/sepolicy")) { if (patch_sepolicy("/sepolicy")) {
auto init = raw_data::mmap_rw("/init"); auto init = raw_data::mmap_rw("/init");
init.patch({ make_pair(SPLIT_PLAT_CIL, "xxx") }); init.patch({ make_pair(SPLIT_PLAT_CIL, "xxx") });
} }
// Handle overlays // Handle overlays
if (access("/overlay.d", F_OK) == 0) { if (access("/overlay.d", F_OK) == 0) {
LOGD("Merge overlay.d\n"); LOGD("Merge overlay.d\n");
load_overlay_rc("/overlay.d"); load_overlay_rc("/overlay.d");
mv_path("/overlay.d", "/"); mv_path("/overlay.d", "/");
} }
patch_init_rc("/init.rc", "/init.p.rc", "/sbin"); patch_init_rc("/init.rc", "/init.p.rc", "/sbin");
rename("/init.p.rc", "/init.rc"); rename("/init.p.rc", "/init.rc");
// Create hardlink mirror of /sbin to /root // Create hardlink mirror of /sbin to /root
mkdir("/root", 0750); mkdir("/root", 0750);
clone_attr("/sbin", "/root"); clone_attr("/sbin", "/root");
link_path("/sbin", "/root"); link_path("/sbin", "/root");
// Dump magiskinit as magisk // Dump magiskinit as magisk
int fd = xopen("/sbin/magisk", O_WRONLY | O_CREAT, 0755); int fd = xopen("/sbin/magisk", O_WRONLY | O_CREAT, 0755);
write(fd, self.buf, self.sz); write(fd, self.buf, self.sz);
close(fd); close(fd);
} }
void MagiskProxy::start() { void MagiskProxy::start() {
// Mount rootfs as rw to do post-init rootfs patches // Mount rootfs as rw to do post-init rootfs patches
xmount(nullptr, "/", nullptr, MS_REMOUNT, nullptr); xmount(nullptr, "/", nullptr, MS_REMOUNT, nullptr);
// Backup stuffs before removing them // Backup stuffs before removing them
self = raw_data::read("/sbin/magisk"); self = raw_data::read("/sbin/magisk");
config = raw_data::read("/.backup/.magisk"); config = raw_data::read("/.backup/.magisk");
char custom_rules_dir[64]; char custom_rules_dir[64];
custom_rules_dir[0] = '\0'; custom_rules_dir[0] = '\0';
xreadlink(TMP_RULESDIR, custom_rules_dir, sizeof(custom_rules_dir)); xreadlink(TMP_RULESDIR, custom_rules_dir, sizeof(custom_rules_dir));
unlink("/sbin/magisk"); unlink("/sbin/magisk");
rm_rf("/.backup"); rm_rf("/.backup");
setup_tmp("/sbin"); setup_tmp("/sbin");
// Create symlinks pointing back to /root // Create symlinks pointing back to /root
recreate_sbin("/root", false); recreate_sbin("/root", false);
if (custom_rules_dir[0]) if (custom_rules_dir[0])
xsymlink(custom_rules_dir, "/sbin/" RULESDIR); xsymlink(custom_rules_dir, "/sbin/" RULESDIR);
// Tell magiskd to remount rootfs // Tell magiskd to remount rootfs
setenv("REMOUNT_ROOT", "1", 1); setenv("REMOUNT_ROOT", "1", 1);
execv("/sbin/magisk", argv); execv("/sbin/magisk", argv);
} }

View File

@@ -9,8 +9,8 @@
using namespace std; using namespace std;
void fstab_entry::to_file(FILE *fp) { void fstab_entry::to_file(FILE *fp) {
fprintf(fp, "%s %s %s %s %s\n", dev.data(), mnt_point.data(), fprintf(fp, "%s %s %s %s %s\n", dev.data(), mnt_point.data(),
type.data(), mnt_flags.data(), fsmgr_flags.data()); type.data(), mnt_flags.data(), fsmgr_flags.data());
} }
#define set_info(val) \ #define set_info(val) \
@@ -22,193 +22,193 @@ entry.val = &line[val##0];
extern uint32_t patch_verity(void *buf, uint32_t size); extern uint32_t patch_verity(void *buf, uint32_t size);
void FirstStageInit::prepare() { void FirstStageInit::prepare() {
if (cmd->force_normal_boot) { if (cmd->force_normal_boot) {
xmkdirs(FSR "/system/bin", 0755); xmkdirs(FSR "/system/bin", 0755);
rename("/init" /* magiskinit */, FSR "/system/bin/init"); rename("/init" /* magiskinit */, FSR "/system/bin/init");
symlink("/system/bin/init", FSR "/init"); symlink("/system/bin/init", FSR "/init");
rename("/.backup", FSR "/.backup"); rename("/.backup", FSR "/.backup");
rename("/overlay.d", FSR "/overlay.d"); rename("/overlay.d", FSR "/overlay.d");
xsymlink("/system/bin/init", "/init"); xsymlink("/system/bin/init", "/init");
chdir(FSR); chdir(FSR);
} else { } else {
xmkdir("/system", 0755); xmkdir("/system", 0755);
xmkdir("/system/bin", 0755); xmkdir("/system/bin", 0755);
rename("/init" /* magiskinit */ , "/system/bin/init"); rename("/init" /* magiskinit */ , "/system/bin/init");
rename("/.backup/init", "/init"); rename("/.backup/init", "/init");
} }
// Try to load fstab from dt // Try to load fstab from dt
vector<fstab_entry> fstab; vector<fstab_entry> fstab;
read_dt_fstab(fstab); read_dt_fstab(fstab);
char fstab_file[128]; char fstab_file[128];
fstab_file[0] = '\0'; fstab_file[0] = '\0';
// Find existing fstab file // Find existing fstab file
for (const char *hw : { cmd->fstab_suffix, cmd->hardware, cmd->hardware_plat }) { for (const char *hw : { cmd->fstab_suffix, cmd->hardware, cmd->hardware_plat }) {
if (hw[0] == '\0') if (hw[0] == '\0')
continue; continue;
sprintf(fstab_file, "fstab.%s", hw); sprintf(fstab_file, "fstab.%s", hw);
if (access(fstab_file, F_OK) != 0) { if (access(fstab_file, F_OK) != 0) {
fstab_file[0] = '\0'; fstab_file[0] = '\0';
continue; continue;
} else { } else {
LOGD("Found fstab file: %s\n", fstab_file); LOGD("Found fstab file: %s\n", fstab_file);
break; break;
} }
} }
if (fstab.empty()) { if (fstab.empty()) {
// fstab has to be somewhere in ramdisk // fstab has to be somewhere in ramdisk
if (fstab_file[0] == '\0') { if (fstab_file[0] == '\0') {
LOGE("Cannot find fstab file in ramdisk!\n"); LOGE("Cannot find fstab file in ramdisk!\n");
return; return;
} }
// Parse and load fstab file // Parse and load fstab file
file_readline(fstab_file, [&](string_view l) -> bool { file_readline(fstab_file, [&](string_view l) -> bool {
if (l[0] == '#' || l.length() == 1) if (l[0] == '#' || l.length() == 1)
return true; return true;
char *line = (char *) l.data(); char *line = (char *) l.data();
int dev0, dev1, mnt_point0, mnt_point1, type0, type1, int dev0, dev1, mnt_point0, mnt_point1, type0, type1,
mnt_flags0, mnt_flags1, fsmgr_flags0, fsmgr_flags1; mnt_flags0, mnt_flags1, fsmgr_flags0, fsmgr_flags1;
sscanf(line, "%n%*s%n %n%*s%n %n%*s%n %n%*s%n %n%*s%n", sscanf(line, "%n%*s%n %n%*s%n %n%*s%n %n%*s%n %n%*s%n",
&dev0, &dev1, &mnt_point0, &mnt_point1, &type0, &type1, &dev0, &dev1, &mnt_point0, &mnt_point1, &type0, &type1,
&mnt_flags0, &mnt_flags1, &fsmgr_flags0, &fsmgr_flags1); &mnt_flags0, &mnt_flags1, &fsmgr_flags0, &fsmgr_flags1);
fstab_entry entry; fstab_entry entry;
set_info(dev); set_info(dev);
set_info(mnt_point); set_info(mnt_point);
set_info(type); set_info(type);
set_info(mnt_flags); set_info(mnt_flags);
set_info(fsmgr_flags); set_info(fsmgr_flags);
fstab.emplace_back(std::move(entry)); fstab.emplace_back(std::move(entry));
return true; return true;
}); });
} else { } else {
// All dt fstab entries should be first_stage_mount // All dt fstab entries should be first_stage_mount
for (auto &entry : fstab) { for (auto &entry : fstab) {
if (!str_contains(entry.fsmgr_flags, "first_stage_mount")) { if (!str_contains(entry.fsmgr_flags, "first_stage_mount")) {
if (!entry.fsmgr_flags.empty()) if (!entry.fsmgr_flags.empty())
entry.fsmgr_flags += ','; entry.fsmgr_flags += ',';
entry.fsmgr_flags += "first_stage_mount"; entry.fsmgr_flags += "first_stage_mount";
} }
} }
// Dump dt fstab to fstab file in rootfs // Dump dt fstab to fstab file in rootfs
if (fstab_file[0] == '\0') { if (fstab_file[0] == '\0') {
const char *suffix = const char *suffix =
cmd->fstab_suffix[0] ? cmd->fstab_suffix : cmd->fstab_suffix[0] ? cmd->fstab_suffix :
(cmd->hardware[0] ? cmd->hardware : (cmd->hardware[0] ? cmd->hardware :
(cmd->hardware_plat[0] ? cmd->hardware_plat : nullptr)); (cmd->hardware_plat[0] ? cmd->hardware_plat : nullptr));
if (suffix == nullptr) { if (suffix == nullptr) {
LOGE("Cannot determine fstab suffix!\n"); LOGE("Cannot determine fstab suffix!\n");
return; return;
} }
sprintf(fstab_file, "fstab.%s", suffix); sprintf(fstab_file, "fstab.%s", suffix);
} }
// Patch init to force IsDtFstabCompatible() return false // Patch init to force IsDtFstabCompatible() return false
auto init = raw_data::mmap_rw("/init"); auto init = raw_data::mmap_rw("/init");
init.patch({ make_pair("android,fstab", "xxx") }); init.patch({ make_pair("android,fstab", "xxx") });
} }
{ {
LOGD("Write fstab file: %s\n", fstab_file); LOGD("Write fstab file: %s\n", fstab_file);
auto fp = xopen_file(fstab_file, "we"); auto fp = xopen_file(fstab_file, "we");
for (auto &entry : fstab) { for (auto &entry : fstab) {
// Redirect system mnt_point so init won't switch root in first stage init // Redirect system mnt_point so init won't switch root in first stage init
if (entry.mnt_point == "/system") if (entry.mnt_point == "/system")
entry.mnt_point = "/system_root"; entry.mnt_point = "/system_root";
// Force remove AVB for 2SI since it may bootloop some devices // Force remove AVB for 2SI since it may bootloop some devices
auto len = patch_verity(entry.fsmgr_flags.data(), entry.fsmgr_flags.length()); auto len = patch_verity(entry.fsmgr_flags.data(), entry.fsmgr_flags.length());
entry.fsmgr_flags.resize(len); entry.fsmgr_flags.resize(len);
entry.to_file(fp.get()); entry.to_file(fp.get());
} }
} }
chmod(fstab_file, 0644); chmod(fstab_file, 0644);
chdir("/"); chdir("/");
} }
#define INIT_PATH "/system/bin/init" #define INIT_PATH "/system/bin/init"
#define REDIR_PATH "/system/bin/am" #define REDIR_PATH "/system/bin/am"
void SARInit::first_stage_prep() { void SARInit::first_stage_prep() {
int pid = getpid(); int pid = getpid();
xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755"); xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755");
// Patch init binary // Patch init binary
int src = xopen("/init", O_RDONLY); int src = xopen("/init", O_RDONLY);
int dest = xopen("/dev/init", O_CREAT | O_WRONLY, 0); int dest = xopen("/dev/init", O_CREAT | O_WRONLY, 0);
{ {
auto init = raw_data::read(src); auto init = raw_data::read(src);
init.patch({ make_pair(INIT_PATH, REDIR_PATH) }); init.patch({ make_pair(INIT_PATH, REDIR_PATH) });
write(dest, init.buf, init.sz); write(dest, init.buf, init.sz);
fclone_attr(src, dest); fclone_attr(src, dest);
close(dest); close(dest);
} }
// Replace redirect init with magiskinit // Replace redirect init with magiskinit
dest = xopen("/dev/magiskinit", O_CREAT | O_WRONLY, 0); dest = xopen("/dev/magiskinit", O_CREAT | O_WRONLY, 0);
write(dest, self.buf, self.sz); write(dest, self.buf, self.sz);
fclone_attr(src, dest); fclone_attr(src, dest);
close(src); close(src);
close(dest); close(dest);
xmount("/dev/init", "/init", nullptr, MS_BIND, nullptr); xmount("/dev/init", "/init", nullptr, MS_BIND, nullptr);
xmount("/dev/magiskinit", REDIR_PATH, nullptr, MS_BIND, nullptr); xmount("/dev/magiskinit", REDIR_PATH, nullptr, MS_BIND, nullptr);
xumount2("/dev", MNT_DETACH); xumount2("/dev", MNT_DETACH);
// Block SIGUSR1 // Block SIGUSR1
sigset_t block, old; sigset_t block, old;
sigemptyset(&block); sigemptyset(&block);
sigaddset(&block, SIGUSR1); sigaddset(&block, SIGUSR1);
sigprocmask(SIG_BLOCK, &block, &old); sigprocmask(SIG_BLOCK, &block, &old);
if (int child = xfork(); child) { if (int child = xfork(); child) {
LOGD("init daemon [%d]\n", child); LOGD("init daemon [%d]\n", child);
// Wait for children signal // Wait for children signal
int sig; int sig;
sigwait(&block, &sig); sigwait(&block, &sig);
// Restore sigmask // Restore sigmask
sigprocmask(SIG_SETMASK, &old, nullptr); sigprocmask(SIG_SETMASK, &old, nullptr);
} else { } else {
// Establish socket for 2nd stage ack // Establish socket for 2nd stage ack
struct sockaddr_un sun; struct sockaddr_un sun;
int sockfd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); int sockfd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
xbind(sockfd, (struct sockaddr*) &sun, setup_sockaddr(&sun, INIT_SOCKET)); xbind(sockfd, (struct sockaddr*) &sun, setup_sockaddr(&sun, INIT_SOCKET));
xlisten(sockfd, 1); xlisten(sockfd, 1);
// Resume parent // Resume parent
kill(pid, SIGUSR1); kill(pid, SIGUSR1);
// Wait for second stage ack // Wait for second stage ack
int client = xaccept4(sockfd, nullptr, nullptr, SOCK_CLOEXEC); int client = xaccept4(sockfd, nullptr, nullptr, SOCK_CLOEXEC);
// Write backup files // Write backup files
char *tmp_dir = read_string(client); char *tmp_dir = read_string(client);
chdir(tmp_dir); chdir(tmp_dir);
free(tmp_dir); free(tmp_dir);
int cfg = xopen(INTLROOT "/config", O_WRONLY | O_CREAT, 0); int cfg = xopen(INTLROOT "/config", O_WRONLY | O_CREAT, 0);
xwrite(cfg, config.buf, config.sz); xwrite(cfg, config.buf, config.sz);
close(cfg); close(cfg);
restore_folder(ROOTOVL, overlays); restore_folder(ROOTOVL, overlays);
// Ack and bail out! // Ack and bail out!
write(sockfd, &sockfd, sizeof(sockfd)); write(sockfd, &sockfd, sizeof(sockfd));
close(client); close(client);
close(sockfd); close(sockfd);
exit(0); exit(0);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -9,35 +9,35 @@
*****************/ *****************/
struct mtk_hdr { struct mtk_hdr {
uint32_t magic; /* MTK magic */ uint32_t magic; /* MTK magic */
uint32_t size; /* Size of the content */ uint32_t size; /* Size of the content */
char name[32]; /* The type of the header */ char name[32]; /* The type of the header */
char padding[472]; /* Padding to 512 bytes */ char padding[472]; /* Padding to 512 bytes */
} __attribute__((packed)); } __attribute__((packed));
struct dhtb_hdr { struct dhtb_hdr {
char magic[8]; /* DHTB magic */ char magic[8]; /* DHTB magic */
uint8_t checksum[40]; /* Payload SHA256, whole image + SEANDROIDENFORCE + 0xFFFFFFFF */ uint8_t checksum[40]; /* Payload SHA256, whole image + SEANDROIDENFORCE + 0xFFFFFFFF */
uint32_t size; /* Payload size, whole image + SEANDROIDENFORCE + 0xFFFFFFFF */ uint32_t size; /* Payload size, whole image + SEANDROIDENFORCE + 0xFFFFFFFF */
char padding[460]; /* Padding to 512 bytes */ char padding[460]; /* Padding to 512 bytes */
} __attribute__((packed)); } __attribute__((packed));
struct blob_hdr { struct blob_hdr {
char secure_magic[20]; /* "-SIGNED-BY-SIGNBLOB-" */ char secure_magic[20]; /* "-SIGNED-BY-SIGNBLOB-" */
uint32_t datalen; /* 0x00000000 */ uint32_t datalen; /* 0x00000000 */
uint32_t signature; /* 0x00000000 */ uint32_t signature; /* 0x00000000 */
char magic[16]; /* "MSM-RADIO-UPDATE" */ char magic[16]; /* "MSM-RADIO-UPDATE" */
uint32_t hdr_version; /* 0x00010000 */ uint32_t hdr_version; /* 0x00010000 */
uint32_t hdr_size; /* Size of header */ uint32_t hdr_size; /* Size of header */
uint32_t part_offset; /* Same as size */ uint32_t part_offset; /* Same as size */
uint32_t num_parts; /* Number of partitions */ uint32_t num_parts; /* Number of partitions */
uint32_t unknown[7]; /* All 0x00000000 */ uint32_t unknown[7]; /* All 0x00000000 */
char name[4]; /* Name of partition */ char name[4]; /* Name of partition */
uint32_t offset; /* offset in blob where this partition starts */ uint32_t offset; /* offset in blob where this partition starts */
uint32_t size; /* Size of data */ uint32_t size; /* Size of data */
uint32_t version; /* 0x00000001 */ uint32_t version; /* 0x00000001 */
} __attribute__((packed)); } __attribute__((packed));
@@ -78,54 +78,54 @@ struct blob_hdr {
*/ */
struct boot_img_hdr_common { struct boot_img_hdr_common {
char magic[BOOT_MAGIC_SIZE]; char magic[BOOT_MAGIC_SIZE];
uint32_t kernel_size; /* size in bytes */ uint32_t kernel_size; /* size in bytes */
uint32_t kernel_addr; /* physical load addr */ uint32_t kernel_addr; /* physical load addr */
uint32_t ramdisk_size; /* size in bytes */ uint32_t ramdisk_size; /* size in bytes */
uint32_t ramdisk_addr; /* physical load addr */ uint32_t ramdisk_addr; /* physical load addr */
uint32_t second_size; /* size in bytes */ uint32_t second_size; /* size in bytes */
uint32_t second_addr; /* physical load addr */ uint32_t second_addr; /* physical load addr */
} __attribute__((packed)); } __attribute__((packed));
struct boot_img_hdr_v0 : public boot_img_hdr_common { struct boot_img_hdr_v0 : public boot_img_hdr_common {
uint32_t tags_addr; /* physical addr for kernel tags */ uint32_t tags_addr; /* physical addr for kernel tags */
uint32_t page_size; /* flash page size we assume */ uint32_t page_size; /* flash page size we assume */
// In header v1, this field is used for header version // In header v1, this field is used for header version
// However, on some devices like Samsung, this field is used to store DTB // However, on some devices like Samsung, this field is used to store DTB
// We treat this field differently based on its value // We treat this field differently based on its value
union { union {
uint32_t header_version; /* the version of the header */ uint32_t header_version; /* the version of the header */
uint32_t extra_size; /* extra blob size in bytes */ uint32_t extra_size; /* extra blob size in bytes */
}; };
// Operating system version and security patch level. // Operating system version and security patch level.
// For version "A.B.C" and patch level "Y-M-D": // For version "A.B.C" and patch level "Y-M-D":
// (7 bits for each of A, B, C; 7 bits for (Y-2000), 4 bits for M) // (7 bits for each of A, B, C; 7 bits for (Y-2000), 4 bits for M)
// os_version = A[31:25] B[24:18] C[17:11] (Y-2000)[10:4] M[3:0] // os_version = A[31:25] B[24:18] C[17:11] (Y-2000)[10:4] M[3:0]
uint32_t os_version; uint32_t os_version;
char name[BOOT_NAME_SIZE]; /* asciiz product name */ char name[BOOT_NAME_SIZE]; /* asciiz product name */
char cmdline[BOOT_ARGS_SIZE]; char cmdline[BOOT_ARGS_SIZE];
char id[BOOT_ID_SIZE]; /* timestamp / checksum / sha1 / etc */ char id[BOOT_ID_SIZE]; /* timestamp / checksum / sha1 / etc */
// Supplemental command line data; kept here to maintain // Supplemental command line data; kept here to maintain
// binary compatibility with older versions of mkbootimg. // binary compatibility with older versions of mkbootimg.
char extra_cmdline[BOOT_EXTRA_ARGS_SIZE]; char extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
} __attribute__((packed)); } __attribute__((packed));
struct boot_img_hdr_v1 : public boot_img_hdr_v0 { struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
uint32_t recovery_dtbo_size; /* size in bytes for recovery DTBO/ACPIO image */ uint32_t recovery_dtbo_size; /* size in bytes for recovery DTBO/ACPIO image */
uint64_t recovery_dtbo_offset; /* offset to recovery dtbo/acpio in boot image */ uint64_t recovery_dtbo_offset; /* offset to recovery dtbo/acpio in boot image */
uint32_t header_size; uint32_t header_size;
} __attribute__((packed)); } __attribute__((packed));
struct boot_img_hdr_v2 : public boot_img_hdr_v1 { struct boot_img_hdr_v2 : public boot_img_hdr_v1 {
uint32_t dtb_size; /* size in bytes for DTB image */ uint32_t dtb_size; /* size in bytes for DTB image */
uint64_t dtb_addr; /* physical load address for DTB image */ uint64_t dtb_addr; /* physical load address for DTB image */
} __attribute__((packed)); } __attribute__((packed));
// Default to hdr v2 // Default to hdr v2
@@ -133,16 +133,16 @@ using boot_img_hdr = boot_img_hdr_v2;
// Special Samsung header // Special Samsung header
struct boot_img_hdr_pxa : public boot_img_hdr_common { struct boot_img_hdr_pxa : public boot_img_hdr_common {
uint32_t extra_size; /* extra blob size in bytes */ uint32_t extra_size; /* extra blob size in bytes */
uint32_t unknown; uint32_t unknown;
uint32_t tags_addr; /* physical addr for kernel tags */ uint32_t tags_addr; /* physical addr for kernel tags */
uint32_t page_size; /* flash page size we assume */ uint32_t page_size; /* flash page size we assume */
char name[24]; /* asciiz product name */ char name[24]; /* asciiz product name */
char cmdline[BOOT_ARGS_SIZE]; char cmdline[BOOT_ARGS_SIZE];
char id[BOOT_ID_SIZE]; /* timestamp / checksum / sha1 / etc */ char id[BOOT_ID_SIZE]; /* timestamp / checksum / sha1 / etc */
char extra_cmdline[BOOT_EXTRA_ARGS_SIZE]; char extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
} __attribute__((packed)); } __attribute__((packed));
/* When the boot image header has a version of 3, the structure of the boot /* When the boot image header has a version of 3, the structure of the boot
@@ -178,34 +178,34 @@ struct boot_img_hdr_pxa : public boot_img_hdr_common {
*/ */
struct boot_img_hdr_v3 { struct boot_img_hdr_v3 {
uint8_t magic[BOOT_MAGIC_SIZE]; uint8_t magic[BOOT_MAGIC_SIZE];
uint32_t kernel_size; /* size in bytes */ uint32_t kernel_size; /* size in bytes */
uint32_t ramdisk_size; /* size in bytes */ uint32_t ramdisk_size; /* size in bytes */
uint32_t os_version; uint32_t os_version;
uint32_t header_size; uint32_t header_size;
uint32_t reserved[4]; uint32_t reserved[4];
uint32_t header_version; uint32_t header_version;
char cmdline[BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE]; char cmdline[BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE];
} __attribute__((packed)); } __attribute__((packed));
struct boot_img_hdr_vnd_v3 { struct boot_img_hdr_vnd_v3 {
// Must be VENDOR_BOOT_MAGIC. // Must be VENDOR_BOOT_MAGIC.
uint8_t magic[BOOT_MAGIC_SIZE]; uint8_t magic[BOOT_MAGIC_SIZE];
// Version of the vendor boot image header. // Version of the vendor boot image header.
uint32_t header_version; uint32_t header_version;
uint32_t page_size; /* flash page size we assume */ uint32_t page_size; /* flash page size we assume */
uint32_t kernel_addr; /* physical load addr */ uint32_t kernel_addr; /* physical load addr */
uint32_t ramdisk_addr; /* physical load addr */ uint32_t ramdisk_addr; /* physical load addr */
uint32_t ramdisk_size; /* size in bytes */ uint32_t ramdisk_size; /* size in bytes */
char cmdline[VENDOR_BOOT_ARGS_SIZE]; char cmdline[VENDOR_BOOT_ARGS_SIZE];
uint32_t tags_addr; /* physical addr for kernel tags (if required) */ uint32_t tags_addr; /* physical addr for kernel tags (if required) */
char name[BOOT_NAME_SIZE]; /* asciiz product name */ char name[BOOT_NAME_SIZE]; /* asciiz product name */
uint32_t header_size; uint32_t header_size;
uint32_t dtb_size; /* size in bytes for DTB image */ uint32_t dtb_size; /* size in bytes for DTB image */
uint64_t dtb_addr; /* physical load address for DTB image */ uint64_t dtb_addr; /* physical load address for DTB image */
} __attribute__((packed)); } __attribute__((packed));
/******************************* /*******************************
@@ -219,51 +219,51 @@ virtual type name() { return 0; }
struct dyn_img_hdr { struct dyn_img_hdr {
// Standard entries // Standard entries
decl_var(kernel_size, 32) decl_var(kernel_size, 32)
decl_var(ramdisk_size, 32) decl_var(ramdisk_size, 32)
decl_var(second_size, 32) decl_var(second_size, 32)
decl_var(page_size, 32) decl_var(page_size, 32)
decl_val(header_version, uint32_t) decl_val(header_version, uint32_t)
decl_var(extra_size, 32) decl_var(extra_size, 32)
decl_var(os_version, 32) decl_var(os_version, 32)
decl_val(name, char *) decl_val(name, char *)
decl_val(cmdline, char *) decl_val(cmdline, char *)
decl_val(id, char *) decl_val(id, char *)
decl_val(extra_cmdline, char *) decl_val(extra_cmdline, char *)
// v1/v2 specific // v1/v2 specific
decl_var(recovery_dtbo_size, 32) decl_var(recovery_dtbo_size, 32)
decl_var(recovery_dtbo_offset, 64) decl_var(recovery_dtbo_offset, 64)
decl_var(header_size, 32) decl_var(header_size, 32)
decl_var(dtb_size, 32) decl_var(dtb_size, 32)
virtual ~dyn_img_hdr() { virtual ~dyn_img_hdr() {
free(raw); free(raw);
} }
virtual size_t hdr_size() = 0; virtual size_t hdr_size() = 0;
virtual size_t hdr_space() { return page_size(); } virtual size_t hdr_space() { return page_size(); }
const void *raw_hdr() const { return raw; } const void *raw_hdr() const { return raw; }
void print(); void print();
void dump_hdr_file(); void dump_hdr_file();
void load_hdr_file(); void load_hdr_file();
protected: protected:
union { union {
// Main header could be either AOSP or PXA // Main header could be either AOSP or PXA
boot_img_hdr_v2 *v2_hdr; /* AOSP v2 header */ boot_img_hdr_v2 *v2_hdr; /* AOSP v2 header */
boot_img_hdr_v3 *v3_hdr; /* AOSP v3 header */ boot_img_hdr_v3 *v3_hdr; /* AOSP v3 header */
boot_img_hdr_pxa *hdr_pxa; /* Samsung PXA header */ boot_img_hdr_pxa *hdr_pxa; /* Samsung PXA header */
boot_img_hdr_vnd_v3 *vnd; /* AOSP vendor v3 header */ boot_img_hdr_vnd_v3 *vnd; /* AOSP vendor v3 header */
void *raw; /* Raw pointer */ void *raw; /* Raw pointer */
}; };
private: private:
// Junk for references // Junk for references
static uint32_t j32; static uint32_t j32;
static uint64_t j64; static uint64_t j64;
}; };
#undef decl_var #undef decl_var
@@ -273,8 +273,8 @@ private:
protected: name() = default; \ protected: name() = default; \
public: \ public: \
name(void *ptr) { \ name(void *ptr) { \
raw = xmalloc(sizeof(hdr)); \ raw = xmalloc(sizeof(hdr)); \
memcpy(raw, ptr, sizeof(hdr)); \ memcpy(raw, ptr, sizeof(hdr)); \
} \ } \
size_t hdr_size() override { return sizeof(hdr); } size_t hdr_size() override { return sizeof(hdr); }
@@ -285,93 +285,93 @@ decltype(std::declval<dyn_img_hdr>().name()) name() override { return hdr_name->
#define impl_val(name) __impl_val(name, v2_hdr) #define impl_val(name) __impl_val(name, v2_hdr)
struct dyn_img_common : public dyn_img_hdr { struct dyn_img_common : public dyn_img_hdr {
impl_val(kernel_size) impl_val(kernel_size)
impl_val(ramdisk_size) impl_val(ramdisk_size)
impl_val(second_size) impl_val(second_size)
}; };
struct dyn_img_v0 : public dyn_img_common { struct dyn_img_v0 : public dyn_img_common {
impl_cls(v0) impl_cls(v0)
impl_val(page_size) impl_val(page_size)
impl_val(extra_size) impl_val(extra_size)
impl_val(os_version) impl_val(os_version)
impl_val(name) impl_val(name)
impl_val(cmdline) impl_val(cmdline)
impl_val(id) impl_val(id)
impl_val(extra_cmdline) impl_val(extra_cmdline)
}; };
struct dyn_img_v1 : public dyn_img_v0 { struct dyn_img_v1 : public dyn_img_v0 {
impl_cls(v1) impl_cls(v1)
impl_val(header_version) impl_val(header_version)
impl_val(recovery_dtbo_size) impl_val(recovery_dtbo_size)
impl_val(recovery_dtbo_offset) impl_val(recovery_dtbo_offset)
impl_val(header_size) impl_val(header_size)
uint32_t &extra_size() override { return dyn_img_hdr::extra_size(); } uint32_t &extra_size() override { return dyn_img_hdr::extra_size(); }
}; };
struct dyn_img_v2 : public dyn_img_v1 { struct dyn_img_v2 : public dyn_img_v1 {
impl_cls(v2) impl_cls(v2)
impl_val(dtb_size) impl_val(dtb_size)
}; };
#undef impl_val #undef impl_val
#define impl_val(name) __impl_val(name, hdr_pxa) #define impl_val(name) __impl_val(name, hdr_pxa)
struct dyn_img_pxa : public dyn_img_common { struct dyn_img_pxa : public dyn_img_common {
impl_cls(pxa) impl_cls(pxa)
impl_val(extra_size) impl_val(extra_size)
impl_val(page_size) impl_val(page_size)
impl_val(name) impl_val(name)
impl_val(cmdline) impl_val(cmdline)
impl_val(id) impl_val(id)
impl_val(extra_cmdline) impl_val(extra_cmdline)
}; };
#undef impl_val #undef impl_val
#define impl_val(name) __impl_val(name, v3_hdr) #define impl_val(name) __impl_val(name, v3_hdr)
struct dyn_img_v3 : public dyn_img_hdr { struct dyn_img_v3 : public dyn_img_hdr {
impl_cls(v3) impl_cls(v3)
impl_val(kernel_size) impl_val(kernel_size)
impl_val(ramdisk_size) impl_val(ramdisk_size)
impl_val(os_version) impl_val(os_version)
impl_val(header_size) impl_val(header_size)
impl_val(header_version) impl_val(header_version)
impl_val(cmdline) impl_val(cmdline)
// Make API compatible // Make API compatible
uint32_t &page_size() override { page_sz = 4096; return page_sz; } uint32_t &page_size() override { page_sz = 4096; return page_sz; }
char *extra_cmdline() override { return &v3_hdr->cmdline[BOOT_ARGS_SIZE]; } char *extra_cmdline() override { return &v3_hdr->cmdline[BOOT_ARGS_SIZE]; }
private: private:
uint32_t page_sz; uint32_t page_sz;
}; };
#undef impl_val #undef impl_val
#define impl_val(name) __impl_val(name, vnd) #define impl_val(name) __impl_val(name, vnd)
struct dyn_img_vnd_v3 : public dyn_img_hdr { struct dyn_img_vnd_v3 : public dyn_img_hdr {
impl_cls(vnd_v3) impl_cls(vnd_v3)
impl_val(header_version) impl_val(header_version)
impl_val(page_size) impl_val(page_size)
impl_val(ramdisk_size) impl_val(ramdisk_size)
impl_val(cmdline) impl_val(cmdline)
impl_val(name) impl_val(name)
impl_val(header_size) impl_val(header_size)
impl_val(dtb_size) impl_val(dtb_size)
size_t hdr_space() override { auto sz = page_size(); return do_align(hdr_size(), sz); } size_t hdr_space() override { auto sz = page_size(); return do_align(hdr_size(), sz); }
// Make API compatible // Make API compatible
char *extra_cmdline() override { return &vnd->cmdline[BOOT_ARGS_SIZE]; } char *extra_cmdline() override { return &vnd->cmdline[BOOT_ARGS_SIZE]; }
}; };
#undef __impl_cls #undef __impl_cls
@@ -395,48 +395,48 @@ struct dyn_img_vnd_v3 : public dyn_img_hdr {
#define ACCLAIM_FLAG (1 << 9) #define ACCLAIM_FLAG (1 << 9)
struct boot_img { struct boot_img {
// Memory map of the whole image // Memory map of the whole image
uint8_t *map_addr; uint8_t *map_addr;
size_t map_size; size_t map_size;
// Android image header // Android image header
dyn_img_hdr *hdr; dyn_img_hdr *hdr;
// Flags to indicate the state of current boot image // Flags to indicate the state of current boot image
uint16_t flags = 0; uint16_t flags = 0;
// The format of kernel, ramdisk and extra // The format of kernel, ramdisk and extra
format_t k_fmt = UNKNOWN; format_t k_fmt = UNKNOWN;
format_t r_fmt = UNKNOWN; format_t r_fmt = UNKNOWN;
format_t e_fmt = UNKNOWN; format_t e_fmt = UNKNOWN;
/*************************************************** /***************************************************
* Following pointers points within the mmap region * Following pointers points within the mmap region
***************************************************/ ***************************************************/
// MTK headers // MTK headers
mtk_hdr *k_hdr; mtk_hdr *k_hdr;
mtk_hdr *r_hdr; mtk_hdr *r_hdr;
// Pointer to dtb that is embedded in kernel // Pointer to dtb that is embedded in kernel
uint8_t *kernel_dtb; uint8_t *kernel_dtb;
uint32_t kernel_dt_size = 0; uint32_t kernel_dt_size = 0;
// Pointer to end of image // Pointer to end of image
uint8_t *tail; uint8_t *tail;
size_t tail_size = 0; size_t tail_size = 0;
// Pointers to blocks defined in header // Pointers to blocks defined in header
uint8_t *hdr_addr; uint8_t *hdr_addr;
uint8_t *kernel; uint8_t *kernel;
uint8_t *ramdisk; uint8_t *ramdisk;
uint8_t *second; uint8_t *second;
uint8_t *extra; uint8_t *extra;
uint8_t *recovery_dtbo; uint8_t *recovery_dtbo;
uint8_t *dtb; uint8_t *dtb;
boot_img(const char *); boot_img(const char *);
~boot_img(); ~boot_img();
void parse_image(uint8_t *addr, format_t type); void parse_image(uint8_t *addr, format_t type);
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -18,437 +18,437 @@ constexpr int MAX_DEPTH = 32;
static bitset<MAX_DEPTH> depth_set; static bitset<MAX_DEPTH> depth_set;
static void pretty_node(int depth) { static void pretty_node(int depth) {
if (depth == 0) if (depth == 0)
return; return;
for (int i = 0; i < depth - 1; ++i) for (int i = 0; i < depth - 1; ++i)
printf(depth_set[i] ? "" : " "); printf(depth_set[i] ? "" : " ");
printf(depth_set[depth - 1] ? "├── " : "└── "); printf(depth_set[depth - 1] ? "├── " : "└── ");
} }
static void pretty_prop(int depth) { static void pretty_prop(int depth) {
for (int i = 0; i < depth; ++i) for (int i = 0; i < depth; ++i)
printf(depth_set[i] ? "" : " "); printf(depth_set[i] ? "" : " ");
printf(depth_set[depth] ? "" : " "); printf(depth_set[depth] ? "" : " ");
} }
static void print_node(const void *fdt, int node = 0, int depth = 0) { static void print_node(const void *fdt, int node = 0, int depth = 0) {
// Print node itself // Print node itself
pretty_node(depth); pretty_node(depth);
printf("#%d: %s\n", node, fdt_get_name(fdt, node, nullptr)); printf("#%d: %s\n", node, fdt_get_name(fdt, node, nullptr));
// Print properties // Print properties
depth_set[depth] = fdt_first_subnode(fdt, node) >= 0; depth_set[depth] = fdt_first_subnode(fdt, node) >= 0;
int prop; int prop;
fdt_for_each_property_offset(prop, fdt, node) { fdt_for_each_property_offset(prop, fdt, node) {
pretty_prop(depth); pretty_prop(depth);
int size; int size;
const char *name; const char *name;
auto value = static_cast<const char *>(fdt_getprop_by_offset(fdt, prop, &name, &size)); auto value = static_cast<const char *>(fdt_getprop_by_offset(fdt, prop, &name, &size));
bool is_str = !(size > 1 && value[0] == 0); bool is_str = !(size > 1 && value[0] == 0);
if (is_str) { if (is_str) {
// Scan through value to see if printable // Scan through value to see if printable
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
char c = value[i]; char c = value[i];
if (i == size - 1) { if (i == size - 1) {
// Make sure null terminate // Make sure null terminate
is_str = c == '\0'; is_str = c == '\0';
} else if ((c > 0 && c < 32) || c >= 127) { } else if ((c > 0 && c < 32) || c >= 127) {
is_str = false; is_str = false;
break; break;
} }
} }
} }
if (is_str) { if (is_str) {
printf("[%s]: [%s]\n", name, value); printf("[%s]: [%s]\n", name, value);
} else { } else {
printf("[%s]: <bytes>(%d)\n", name, size); printf("[%s]: <bytes>(%d)\n", name, size);
} }
} }
// Recursive // Recursive
if (depth_set[depth]) { if (depth_set[depth]) {
int child; int child;
int prev = -1; int prev = -1;
fdt_for_each_subnode(child, fdt, node) { fdt_for_each_subnode(child, fdt, node) {
if (prev >= 0) if (prev >= 0)
print_node(fdt, prev, depth + 1); print_node(fdt, prev, depth + 1);
prev = child; prev = child;
} }
depth_set[depth] = false; depth_set[depth] = false;
print_node(fdt, prev, depth + 1); print_node(fdt, prev, depth + 1);
} }
} }
static int find_fstab(const void *fdt, int node = 0) { static int find_fstab(const void *fdt, int node = 0) {
if (fdt_get_name(fdt, node, nullptr) == "fstab"sv) if (fdt_get_name(fdt, node, nullptr) == "fstab"sv)
return node; return node;
int child; int child;
fdt_for_each_subnode(child, fdt, node) { fdt_for_each_subnode(child, fdt, node) {
int fstab = find_fstab(fdt, child); int fstab = find_fstab(fdt, child);
if (fstab >= 0) if (fstab >= 0)
return fstab; return fstab;
} }
return -1; return -1;
} }
static void dtb_print(const char *file, bool fstab) { static void dtb_print(const char *file, bool fstab) {
size_t size; size_t size;
uint8_t *dtb; uint8_t *dtb;
fprintf(stderr, "Loading dtbs from [%s]\n", file); fprintf(stderr, "Loading dtbs from [%s]\n", file);
mmap_ro(file, dtb, size); mmap_ro(file, dtb, size);
// Loop through all the dtbs // Loop through all the dtbs
int dtb_num = 0; int dtb_num = 0;
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
if (memcmp(dtb + i, FDT_MAGIC_STR, 4) == 0) { if (memcmp(dtb + i, FDT_MAGIC_STR, 4) == 0) {
auto fdt = dtb + i; auto fdt = dtb + i;
if (fstab) { if (fstab) {
int node = find_fstab(fdt); int node = find_fstab(fdt);
if (node >= 0) { if (node >= 0) {
fprintf(stderr, "Found fstab in dtb.%04d\n", dtb_num); fprintf(stderr, "Found fstab in dtb.%04d\n", dtb_num);
print_node(fdt, node); print_node(fdt, node);
} }
} else { } else {
fprintf(stderr, "Printing dtb.%04d\n", dtb_num); fprintf(stderr, "Printing dtb.%04d\n", dtb_num);
print_node(fdt); print_node(fdt);
} }
++dtb_num; ++dtb_num;
i += fdt_totalsize(fdt) - 1; i += fdt_totalsize(fdt) - 1;
} }
} }
fprintf(stderr, "\n"); fprintf(stderr, "\n");
munmap(dtb, size); munmap(dtb, size);
} }
static bool dtb_patch(const char *file) { static bool dtb_patch(const char *file) {
bool keepverity = check_env("KEEPVERITY"); bool keepverity = check_env("KEEPVERITY");
bool patched = false; bool patched = false;
size_t size; size_t size;
uint8_t *dtb; uint8_t *dtb;
fprintf(stderr, "Loading dtbs from [%s]\n", file); fprintf(stderr, "Loading dtbs from [%s]\n", file);
mmap_rw(file, dtb, size); mmap_rw(file, dtb, size);
// Loop through all the dtbs // Loop through all the dtbs
int dtb_num = 0; int dtb_num = 0;
for (int i = 0; i < size; ++i) { for (int i = 0; i < size; ++i) {
if (memcmp(dtb + i, FDT_MAGIC_STR, 4) == 0) { if (memcmp(dtb + i, FDT_MAGIC_STR, 4) == 0) {
auto fdt = dtb + i; auto fdt = dtb + i;
fprintf(stderr, "Loading dtb.%04d\n", dtb_num); fprintf(stderr, "Loading dtb.%04d\n", dtb_num);
if (int fstab = find_fstab(fdt); fstab >= 0) { if (int fstab = find_fstab(fdt); fstab >= 0) {
int node; int node;
fdt_for_each_subnode(node, fdt, fstab) { fdt_for_each_subnode(node, fdt, fstab) {
const char *name = fdt_get_name(fdt, node, nullptr); const char *name = fdt_get_name(fdt, node, nullptr);
fprintf(stderr, "Found fstab entry [%s]\n", name); fprintf(stderr, "Found fstab entry [%s]\n", name);
if (!keepverity) { if (!keepverity) {
int len; int len;
auto value = fdt_getprop(fdt, node, "fsmgr_flags", &len); auto value = fdt_getprop(fdt, node, "fsmgr_flags", &len);
patched |= patch_verity(const_cast<void *>(value), len) != len; patched |= patch_verity(const_cast<void *>(value), len) != len;
} }
} }
} }
++dtb_num; ++dtb_num;
i += fdt_totalsize(fdt) - 1; i += fdt_totalsize(fdt) - 1;
} }
} }
fprintf(stderr, "\n"); fprintf(stderr, "\n");
munmap(dtb, size); munmap(dtb, size);
return patched; return patched;
} }
int dtb_commands(int argc, char *argv[]) { int dtb_commands(int argc, char *argv[]) {
char *dtb = argv[0]; char *dtb = argv[0];
++argv; ++argv;
--argc; --argc;
if (argv[0] == "print"sv) { if (argv[0] == "print"sv) {
dtb_print(dtb, argc > 1 && argv[1] == "-f"sv); dtb_print(dtb, argc > 1 && argv[1] == "-f"sv);
return 0; return 0;
} else if (argv[0] == "patch"sv) { } else if (argv[0] == "patch"sv) {
if (!dtb_patch(dtb)) if (!dtb_patch(dtb))
exit(1); exit(1);
return 0; return 0;
} else { } else {
return 1; return 1;
} }
} }
namespace { namespace {
// Unused, but keep these precious code as they took TONs of effort to write // Unused, but keep these precious code as they took TONs of effort to write
struct fdt_blob { struct fdt_blob {
void *fdt; void *fdt;
uint32_t offset; uint32_t offset;
uint32_t len; uint32_t len;
}; };
template <class Iter> template <class Iter>
class fdt_map_iter { class fdt_map_iter {
public: public:
typedef decltype(std::declval<typename Iter::value_type::second_type>().fdt) value_type; typedef decltype(std::declval<typename Iter::value_type::second_type>().fdt) value_type;
typedef value_type* pointer; typedef value_type* pointer;
typedef value_type& reference; typedef value_type& reference;
explicit fdt_map_iter(Iter j) : i(j) {} explicit fdt_map_iter(Iter j) : i(j) {}
fdt_map_iter& operator++() { ++i; return *this; } fdt_map_iter& operator++() { ++i; return *this; }
fdt_map_iter operator++(int) { auto tmp = *this; ++(*this); return tmp; } fdt_map_iter operator++(int) { auto tmp = *this; ++(*this); return tmp; }
fdt_map_iter& operator--() { --i; return *this; } fdt_map_iter& operator--() { --i; return *this; }
fdt_map_iter operator--(int) { auto tmp = *this; --(*this); return tmp; } fdt_map_iter operator--(int) { auto tmp = *this; --(*this); return tmp; }
bool operator==(fdt_map_iter j) const { return i == j.i; } bool operator==(fdt_map_iter j) const { return i == j.i; }
bool operator!=(fdt_map_iter j) const { return !(*this == j); } bool operator!=(fdt_map_iter j) const { return !(*this == j); }
reference operator*() { return i->second.fdt; } reference operator*() { return i->second.fdt; }
pointer operator->() { return &i->second.fdt; } pointer operator->() { return &i->second.fdt; }
private: private:
Iter i; Iter i;
}; };
template<class Iter> template<class Iter>
inline fdt_map_iter<Iter> make_iter(Iter j) { return fdt_map_iter<Iter>(j); } inline fdt_map_iter<Iter> make_iter(Iter j) { return fdt_map_iter<Iter>(j); }
template <typename Iter> template <typename Iter>
static bool fdt_patch(Iter first, Iter last) { static bool fdt_patch(Iter first, Iter last) {
bool keepverity = check_env("KEEPVERITY"); bool keepverity = check_env("KEEPVERITY");
bool redirect = check_env("TWOSTAGEINIT"); bool redirect = check_env("TWOSTAGEINIT");
bool modified = false; bool modified = false;
int idx = 0; int idx = 0;
for (auto it = first; it != last; ++it) { for (auto it = first; it != last; ++it) {
++idx; ++idx;
auto fdt = *it; auto fdt = *it;
int fstab = find_fstab(fdt); int fstab = find_fstab(fdt);
if (fstab < 0) if (fstab < 0)
continue; continue;
fprintf(stderr, "Found fstab in dtb.%04d\n", idx - 1); fprintf(stderr, "Found fstab in dtb.%04d\n", idx - 1);
int block; int block;
fdt_for_each_subnode(block, fdt, fstab) { fdt_for_each_subnode(block, fdt, fstab) {
const char *name = fdt_get_name(fdt, block, nullptr); const char *name = fdt_get_name(fdt, block, nullptr);
fprintf(stderr, "Found entry [%s] in fstab\n", name); fprintf(stderr, "Found entry [%s] in fstab\n", name);
if (!keepverity) { if (!keepverity) {
int size; int size;
auto value = fdt_getprop(fdt, block, "fsmgr_flags", &size); auto value = fdt_getprop(fdt, block, "fsmgr_flags", &size);
char *copy = static_cast<char *>(memcpy(malloc(size), value, size)); char *copy = static_cast<char *>(memcpy(malloc(size), value, size));
if (patch_verity(copy, size) != size) { if (patch_verity(copy, size) != size) {
modified = true; modified = true;
fdt_setprop_string(fdt, block, "fsmgr_flags", copy); fdt_setprop_string(fdt, block, "fsmgr_flags", copy);
} }
free(copy); free(copy);
} }
if (redirect && name == "system"sv) { if (redirect && name == "system"sv) {
modified = true; modified = true;
fprintf(stderr, "Changing mnt_point to /system_root\n"); fprintf(stderr, "Changing mnt_point to /system_root\n");
fdt_setprop_string(fdt, block, "mnt_point", "/system_root"); fdt_setprop_string(fdt, block, "mnt_point", "/system_root");
} }
} }
} }
return modified; return modified;
} }
#define MAX_FDT_GROWTH 256 #define MAX_FDT_GROWTH 256
template <class Table, class Header> template <class Table, class Header>
static int dt_table_patch(const Header *hdr, const char *out) { static int dt_table_patch(const Header *hdr, const char *out) {
map<uint32_t, fdt_blob> dtb_map; map<uint32_t, fdt_blob> dtb_map;
auto buf = reinterpret_cast<const uint8_t *>(hdr); auto buf = reinterpret_cast<const uint8_t *>(hdr);
auto tables = reinterpret_cast<const Table *>(hdr + 1); auto tables = reinterpret_cast<const Table *>(hdr + 1);
constexpr bool is_dt_table = std::is_same_v<Header, dt_table_header>; constexpr bool is_dt_table = std::is_same_v<Header, dt_table_header>;
using endian_conv = uint32_t (*)(uint32_t); using endian_conv = uint32_t (*)(uint32_t);
endian_conv be_to_le; endian_conv be_to_le;
endian_conv le_to_be; endian_conv le_to_be;
if constexpr (is_dt_table) { if constexpr (is_dt_table) {
be_to_le = fdt32_to_cpu; be_to_le = fdt32_to_cpu;
le_to_be = cpu_to_fdt32; le_to_be = cpu_to_fdt32;
} else { } else {
be_to_le = le_to_be = [](uint32_t x) -> auto { return x; }; be_to_le = le_to_be = [](uint32_t x) -> auto { return x; };
} }
// Collect all dtbs // Collect all dtbs
auto num_dtb = be_to_le(hdr->num_dtbs); auto num_dtb = be_to_le(hdr->num_dtbs);
for (int i = 0; i < num_dtb; ++i) { for (int i = 0; i < num_dtb; ++i) {
auto offset = be_to_le(tables[i].offset); auto offset = be_to_le(tables[i].offset);
if (dtb_map.count(offset) == 0) { if (dtb_map.count(offset) == 0) {
auto blob = buf + offset; auto blob = buf + offset;
uint32_t size = fdt_totalsize(blob); uint32_t size = fdt_totalsize(blob);
auto fdt = xmalloc(size + MAX_FDT_GROWTH); auto fdt = xmalloc(size + MAX_FDT_GROWTH);
memcpy(fdt, blob, size); memcpy(fdt, blob, size);
fdt_open_into(fdt, fdt, size + MAX_FDT_GROWTH); fdt_open_into(fdt, fdt, size + MAX_FDT_GROWTH);
dtb_map[offset] = { fdt, offset }; dtb_map[offset] = { fdt, offset };
} }
} }
if (dtb_map.empty()) if (dtb_map.empty())
return 1; return 1;
// Patch fdt // Patch fdt
if (!fdt_patch(make_iter(dtb_map.begin()), make_iter(dtb_map.end()))) if (!fdt_patch(make_iter(dtb_map.begin()), make_iter(dtb_map.end())))
return 1; return 1;
unlink(out); unlink(out);
int fd = xopen(out, O_RDWR | O_CREAT | O_CLOEXEC, 0644); int fd = xopen(out, O_RDWR | O_CREAT | O_CLOEXEC, 0644);
uint32_t total_size = 0; uint32_t total_size = 0;
// Copy headers and tables // Copy headers and tables
total_size += xwrite(fd, buf, dtb_map.begin()->first); total_size += xwrite(fd, buf, dtb_map.begin()->first);
// mmap rw to patch table values retroactively // mmap rw to patch table values retroactively
auto mmap_sz = lseek(fd, 0, SEEK_CUR); auto mmap_sz = lseek(fd, 0, SEEK_CUR);
auto addr = (uint8_t *) xmmap(nullptr, mmap_sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); auto addr = (uint8_t *) xmmap(nullptr, mmap_sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// Guess alignment using gcd // Guess alignment using gcd
uint32_t align = 1; uint32_t align = 1;
if constexpr (!is_dt_table) { if constexpr (!is_dt_table) {
auto it = dtb_map.begin(); auto it = dtb_map.begin();
align = (it++)->first; align = (it++)->first;
for (; it != dtb_map.end(); ++it) for (; it != dtb_map.end(); ++it)
align = binary_gcd(align, it->first); align = binary_gcd(align, it->first);
} }
// Write dtbs // Write dtbs
for (auto &val : dtb_map) { for (auto &val : dtb_map) {
val.second.offset = lseek(fd, 0, SEEK_CUR); val.second.offset = lseek(fd, 0, SEEK_CUR);
auto fdt = val.second.fdt; auto fdt = val.second.fdt;
fdt_pack(fdt); fdt_pack(fdt);
auto size = fdt_totalsize(fdt); auto size = fdt_totalsize(fdt);
total_size += xwrite(fd, fdt, size); total_size += xwrite(fd, fdt, size);
val.second.len = do_align(size, align); val.second.len = do_align(size, align);
write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR), align)); write_zero(fd, align_off(lseek(fd, 0, SEEK_CUR), align));
// total_size += align_off(lseek(fd, 0, SEEK_CUR), align); /* Not needed */ // total_size += align_off(lseek(fd, 0, SEEK_CUR), align); /* Not needed */
free(fdt); free(fdt);
} }
// Patch headers // Patch headers
if constexpr (is_dt_table) { if constexpr (is_dt_table) {
auto hdr_rw = reinterpret_cast<Header *>(addr); auto hdr_rw = reinterpret_cast<Header *>(addr);
hdr_rw->total_size = le_to_be(total_size); hdr_rw->total_size = le_to_be(total_size);
} }
auto tables_rw = reinterpret_cast<Table *>(addr + sizeof(Header)); auto tables_rw = reinterpret_cast<Table *>(addr + sizeof(Header));
for (int i = 0; i < num_dtb; ++i) { for (int i = 0; i < num_dtb; ++i) {
auto &blob = dtb_map[be_to_le(tables_rw[i].offset)]; auto &blob = dtb_map[be_to_le(tables_rw[i].offset)];
tables_rw[i].offset = le_to_be(blob.offset); tables_rw[i].offset = le_to_be(blob.offset);
tables_rw[i].len = le_to_be(blob.len); tables_rw[i].len = le_to_be(blob.len);
} }
munmap(addr, mmap_sz); munmap(addr, mmap_sz);
close(fd); close(fd);
return 0; return 0;
} }
static int blob_patch(uint8_t *dtb, size_t dtb_sz, const char *out) { static int blob_patch(uint8_t *dtb, size_t dtb_sz, const char *out) {
vector<uint8_t *> fdt_list; vector<uint8_t *> fdt_list;
vector<uint32_t> padding_list; vector<uint32_t> padding_list;
for (int i = 0; i < dtb_sz; ++i) { for (int i = 0; i < dtb_sz; ++i) {
if (memcmp(dtb + i, FDT_MAGIC_STR, 4) == 0) { if (memcmp(dtb + i, FDT_MAGIC_STR, 4) == 0) {
auto len = fdt_totalsize(dtb + i); auto len = fdt_totalsize(dtb + i);
auto fdt = static_cast<uint8_t *>(xmalloc(len + MAX_FDT_GROWTH)); auto fdt = static_cast<uint8_t *>(xmalloc(len + MAX_FDT_GROWTH));
memcpy(fdt, dtb + i, len); memcpy(fdt, dtb + i, len);
fdt_pack(fdt); fdt_pack(fdt);
uint32_t padding = len - fdt_totalsize(fdt); uint32_t padding = len - fdt_totalsize(fdt);
padding_list.push_back(padding); padding_list.push_back(padding);
fdt_open_into(fdt, fdt, len + MAX_FDT_GROWTH); fdt_open_into(fdt, fdt, len + MAX_FDT_GROWTH);
fdt_list.push_back(fdt); fdt_list.push_back(fdt);
i += len - 1; i += len - 1;
} }
} }
if (!fdt_patch(fdt_list.begin(), fdt_list.end())) if (!fdt_patch(fdt_list.begin(), fdt_list.end()))
return 1; return 1;
unlink(out); unlink(out);
int fd = xopen(out, O_WRONLY | O_CREAT | O_CLOEXEC, 0644); int fd = xopen(out, O_WRONLY | O_CREAT | O_CLOEXEC, 0644);
for (int i = 0; i < fdt_list.size(); ++i) { for (int i = 0; i < fdt_list.size(); ++i) {
auto fdt = fdt_list[i]; auto fdt = fdt_list[i];
fdt_pack(fdt); fdt_pack(fdt);
// Only add padding back if it is anything meaningful // Only add padding back if it is anything meaningful
if (padding_list[i] > 4) { if (padding_list[i] > 4) {
auto len = fdt_totalsize(fdt); auto len = fdt_totalsize(fdt);
fdt_set_totalsize(fdt, len + padding_list[i]); fdt_set_totalsize(fdt, len + padding_list[i]);
} }
xwrite(fd, fdt, fdt_totalsize(fdt)); xwrite(fd, fdt, fdt_totalsize(fdt));
free(fdt); free(fdt);
} }
close(fd); close(fd);
return 0; return 0;
} }
#define MATCH(s) (memcmp(dtb, s, sizeof(s) - 1) == 0) #define MATCH(s) (memcmp(dtb, s, sizeof(s) - 1) == 0)
[[maybe_unused]] static int dtb_patch(const char *in, const char *out) { [[maybe_unused]] static int dtb_patch(const char *in, const char *out) {
if (!out) if (!out)
out = in; out = in;
size_t dtb_sz ; size_t dtb_sz ;
uint8_t *dtb; uint8_t *dtb;
fprintf(stderr, "Loading dtbs from [%s]\n", in); fprintf(stderr, "Loading dtbs from [%s]\n", in);
mmap_ro(in, dtb, dtb_sz); mmap_ro(in, dtb, dtb_sz);
run_finally f([&]{ munmap(dtb, dtb_sz); }); run_finally f([&]{ munmap(dtb, dtb_sz); });
if (MATCH(QCDT_MAGIC)) { if (MATCH(QCDT_MAGIC)) {
auto hdr = reinterpret_cast<qcdt_hdr*>(dtb); auto hdr = reinterpret_cast<qcdt_hdr*>(dtb);
switch (hdr->version) { switch (hdr->version) {
case 1: case 1:
fprintf(stderr, "QCDT v1\n"); fprintf(stderr, "QCDT v1\n");
return dt_table_patch<qctable_v1>(hdr, out); return dt_table_patch<qctable_v1>(hdr, out);
case 2: case 2:
fprintf(stderr, "QCDT v2\n"); fprintf(stderr, "QCDT v2\n");
return dt_table_patch<qctable_v2>(hdr, out); return dt_table_patch<qctable_v2>(hdr, out);
case 3: case 3:
fprintf(stderr, "QCDT v3\n"); fprintf(stderr, "QCDT v3\n");
return dt_table_patch<qctable_v3>(hdr, out); return dt_table_patch<qctable_v3>(hdr, out);
default: default:
return 1; return 1;
} }
} else if (MATCH(DTBH_MAGIC)) { } else if (MATCH(DTBH_MAGIC)) {
auto hdr = reinterpret_cast<dtbh_hdr *>(dtb); auto hdr = reinterpret_cast<dtbh_hdr *>(dtb);
switch (hdr->version) { switch (hdr->version) {
case 2: case 2:
fprintf(stderr, "DTBH v2\n"); fprintf(stderr, "DTBH v2\n");
return dt_table_patch<bhtable_v2>(hdr, out); return dt_table_patch<bhtable_v2>(hdr, out);
default: default:
return 1; return 1;
} }
} else if (MATCH(PXADT_MAGIC)) { } else if (MATCH(PXADT_MAGIC)) {
auto hdr = reinterpret_cast<pxadt_hdr *>(dtb); auto hdr = reinterpret_cast<pxadt_hdr *>(dtb);
switch (hdr->version) { switch (hdr->version) {
case 1: case 1:
fprintf(stderr, "PXA-DT v1\n"); fprintf(stderr, "PXA-DT v1\n");
return dt_table_patch<pxatable_v1>(hdr, out); return dt_table_patch<pxatable_v1>(hdr, out);
default: default:
return 1; return 1;
} }
} else if (MATCH(PXA19xx_MAGIC)) { } else if (MATCH(PXA19xx_MAGIC)) {
auto hdr = reinterpret_cast<pxa19xx_hdr *>(dtb); auto hdr = reinterpret_cast<pxa19xx_hdr *>(dtb);
switch (hdr->version) { switch (hdr->version) {
case 1: case 1:
fprintf(stderr, "PXA-19xx v1\n"); fprintf(stderr, "PXA-19xx v1\n");
return dt_table_patch<pxatable_v1>(hdr, out); return dt_table_patch<pxatable_v1>(hdr, out);
default: default:
return 1; return 1;
} }
} else if (MATCH(SPRD_MAGIC)) { } else if (MATCH(SPRD_MAGIC)) {
auto hdr = reinterpret_cast<sprd_hdr *>(dtb); auto hdr = reinterpret_cast<sprd_hdr *>(dtb);
switch (hdr->version) { switch (hdr->version) {
case 1: case 1:
fprintf(stderr, "SPRD v1\n"); fprintf(stderr, "SPRD v1\n");
return dt_table_patch<sprdtable_v1>(hdr, out); return dt_table_patch<sprdtable_v1>(hdr, out);
default: default:
return 1; return 1;
} }
} else if (MATCH(DT_TABLE_MAGIC)) { } else if (MATCH(DT_TABLE_MAGIC)) {
auto hdr = reinterpret_cast<dt_table_header *>(dtb); auto hdr = reinterpret_cast<dt_table_header *>(dtb);
switch (hdr->version) { switch (hdr->version) {
case 0: case 0:
fprintf(stderr, "DT_TABLE v0\n"); fprintf(stderr, "DT_TABLE v0\n");
return dt_table_patch<dt_table_entry>(hdr, out); return dt_table_patch<dt_table_entry>(hdr, out);
default: default:
return 1; return 1;
} }
} else { } else {
return blob_patch(dtb, dtb_sz, out); return blob_patch(dtb, dtb_sz, out);
} }
} }
} }

View File

@@ -11,94 +11,94 @@
#define SPRD_MAGIC "SPRD" #define SPRD_MAGIC "SPRD"
struct qcdt_hdr { struct qcdt_hdr {
char magic[4]; /* "QCDT" */ char magic[4]; /* "QCDT" */
uint32_t version; /* QCDT version */ uint32_t version; /* QCDT version */
uint32_t num_dtbs; /* Number of DTBs */ uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed)); } __attribute__((packed));
struct qctable_v1 { struct qctable_v1 {
uint32_t cpu_info[3]; /* Some CPU info */ uint32_t cpu_info[3]; /* Some CPU info */
uint32_t offset; /* DTB offset in QCDT */ uint32_t offset; /* DTB offset in QCDT */
uint32_t len; /* DTB size */ uint32_t len; /* DTB size */
} __attribute__((packed)); } __attribute__((packed));
struct qctable_v2 { struct qctable_v2 {
uint32_t cpu_info[4]; /* Some CPU info */ uint32_t cpu_info[4]; /* Some CPU info */
uint32_t offset; /* DTB offset in QCDT */ uint32_t offset; /* DTB offset in QCDT */
uint32_t len; /* DTB size */ uint32_t len; /* DTB size */
} __attribute__((packed)); } __attribute__((packed));
struct qctable_v3 { struct qctable_v3 {
uint32_t cpu_info[8]; /* Some CPU info */ uint32_t cpu_info[8]; /* Some CPU info */
uint32_t offset; /* DTB offset in QCDT */ uint32_t offset; /* DTB offset in QCDT */
uint32_t len; /* DTB size */ uint32_t len; /* DTB size */
} __attribute__((packed)); } __attribute__((packed));
struct dtbh_hdr { struct dtbh_hdr {
char magic[4]; /* "DTBH" */ char magic[4]; /* "DTBH" */
uint32_t version; /* DTBH version */ uint32_t version; /* DTBH version */
uint32_t num_dtbs; /* Number of DTBs */ uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed)); } __attribute__((packed));
struct bhtable_v2 { struct bhtable_v2 {
uint32_t cpu_info[5]; /* Some CPU info */ uint32_t cpu_info[5]; /* Some CPU info */
uint32_t offset; /* DTB offset in DTBH */ uint32_t offset; /* DTB offset in DTBH */
uint32_t len; /* DTB size */ uint32_t len; /* DTB size */
uint32_t space; /* 0x00000020 */ uint32_t space; /* 0x00000020 */
} __attribute__((packed)); } __attribute__((packed));
struct pxadt_hdr { struct pxadt_hdr {
char magic[6]; /* "PXA-DT" */ char magic[6]; /* "PXA-DT" */
uint32_t version; /* PXA-* version */ uint32_t version; /* PXA-* version */
uint32_t num_dtbs; /* Number of DTBs */ uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed)); } __attribute__((packed));
struct pxa19xx_hdr { struct pxa19xx_hdr {
char magic[8]; /* "PXA-19xx" */ char magic[8]; /* "PXA-19xx" */
uint32_t version; /* PXA-* version */ uint32_t version; /* PXA-* version */
uint32_t num_dtbs; /* Number of DTBs */ uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed)); } __attribute__((packed));
struct pxatable_v1 { struct pxatable_v1 {
uint32_t cpu_info[2]; /* Some CPU info */ uint32_t cpu_info[2]; /* Some CPU info */
uint32_t offset; /* DTB offset in PXA-* */ uint32_t offset; /* DTB offset in PXA-* */
uint32_t len; /* DTB size */ uint32_t len; /* DTB size */
} __attribute__((packed)); } __attribute__((packed));
struct sprd_hdr { struct sprd_hdr {
char magic[4]; /* "SPRD" */ char magic[4]; /* "SPRD" */
uint32_t version; /* SPRD version */ uint32_t version; /* SPRD version */
uint32_t num_dtbs; /* Number of DTBs */ uint32_t num_dtbs; /* Number of DTBs */
} __attribute__((packed)); } __attribute__((packed));
struct sprdtable_v1 { struct sprdtable_v1 {
uint32_t cpu_info[3]; /* Some CPU info */ uint32_t cpu_info[3]; /* Some CPU info */
uint32_t offset; /* DTB offset in SPRD */ uint32_t offset; /* DTB offset in SPRD */
uint32_t len; /* DTB size */ uint32_t len; /* DTB size */
} __attribute__((packed)); } __attribute__((packed));
/* AOSP DTB/DTBO partition layout */ /* AOSP DTB/DTBO partition layout */
struct dt_table_header { struct dt_table_header {
uint32_t magic; /* DT_TABLE_MAGIC */ uint32_t magic; /* DT_TABLE_MAGIC */
uint32_t total_size; /* includes dt_table_header + all dt_table_entry */ uint32_t total_size; /* includes dt_table_header + all dt_table_entry */
uint32_t header_size; /* sizeof(dt_table_header) */ uint32_t header_size; /* sizeof(dt_table_header) */
uint32_t dt_entry_size; /* sizeof(dt_table_entry) */ uint32_t dt_entry_size; /* sizeof(dt_table_entry) */
uint32_t num_dtbs; /* number of dt_table_entry */ uint32_t num_dtbs; /* number of dt_table_entry */
uint32_t dt_entries_offset; /* offset to the first dt_table_entry */ uint32_t dt_entries_offset; /* offset to the first dt_table_entry */
uint32_t page_size; /* flash page size we assume */ uint32_t page_size; /* flash page size we assume */
uint32_t version; /* DTBO image version */ uint32_t version; /* DTBO image version */
} __attribute__((packed)); } __attribute__((packed));
struct dt_table_entry { struct dt_table_entry {
uint32_t len; /* DTB size */ uint32_t len; /* DTB size */
uint32_t offset; uint32_t offset;
uint32_t id; uint32_t id;
uint32_t rev; uint32_t rev;
uint32_t flags; uint32_t flags;
uint32_t custom[3]; uint32_t custom[3];
} __attribute__((packed)); } __attribute__((packed));

View File

@@ -8,15 +8,15 @@ Fmt2Ext fmt2ext;
class FormatInit { class FormatInit {
public: public:
FormatInit() { FormatInit() {
name2fmt["gzip"] = GZIP; name2fmt["gzip"] = GZIP;
name2fmt["xz"] = XZ; name2fmt["xz"] = XZ;
name2fmt["lzma"] = LZMA; name2fmt["lzma"] = LZMA;
name2fmt["bzip2"] = BZIP2; name2fmt["bzip2"] = BZIP2;
name2fmt["lz4"] = LZ4; name2fmt["lz4"] = LZ4;
name2fmt["lz4_legacy"] = LZ4_LEGACY; name2fmt["lz4_legacy"] = LZ4_LEGACY;
name2fmt["lz4_lg"] = LZ4_LG; name2fmt["lz4_lg"] = LZ4_LG;
} }
}; };
static FormatInit init; static FormatInit init;
@@ -24,82 +24,82 @@ static FormatInit init;
#define MATCH(s) (len >= (sizeof(s) - 1) && memcmp(buf, s, sizeof(s) - 1) == 0) #define MATCH(s) (len >= (sizeof(s) - 1) && memcmp(buf, s, sizeof(s) - 1) == 0)
format_t check_fmt(const void *buf, size_t len) { format_t check_fmt(const void *buf, size_t len) {
if (MATCH(CHROMEOS_MAGIC)) { if (MATCH(CHROMEOS_MAGIC)) {
return CHROMEOS; return CHROMEOS;
} else if (MATCH(BOOT_MAGIC)) { } else if (MATCH(BOOT_MAGIC)) {
return AOSP; return AOSP;
} else if (MATCH(VENDOR_BOOT_MAGIC)) { } else if (MATCH(VENDOR_BOOT_MAGIC)) {
return AOSP_VENDOR; return AOSP_VENDOR;
} else if (MATCH(GZIP1_MAGIC) || MATCH(GZIP2_MAGIC)) { } else if (MATCH(GZIP1_MAGIC) || MATCH(GZIP2_MAGIC)) {
return GZIP; return GZIP;
} else if (MATCH(LZOP_MAGIC)) { } else if (MATCH(LZOP_MAGIC)) {
return LZOP; return LZOP;
} else if (MATCH(XZ_MAGIC)) { } else if (MATCH(XZ_MAGIC)) {
return XZ; return XZ;
} else if (len >= 13 && memcmp(buf, "\x5d\x00\x00", 3) == 0 } else if (len >= 13 && memcmp(buf, "\x5d\x00\x00", 3) == 0
&& (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) { && (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) {
return LZMA; return LZMA;
} else if (MATCH(BZIP_MAGIC)) { } else if (MATCH(BZIP_MAGIC)) {
return BZIP2; return BZIP2;
} else if (MATCH(LZ41_MAGIC) || MATCH(LZ42_MAGIC)) { } else if (MATCH(LZ41_MAGIC) || MATCH(LZ42_MAGIC)) {
return LZ4; return LZ4;
} else if (MATCH(LZ4_LEG_MAGIC)) { } else if (MATCH(LZ4_LEG_MAGIC)) {
return LZ4_LEGACY; return LZ4_LEGACY;
} else if (MATCH(MTK_MAGIC)) { } else if (MATCH(MTK_MAGIC)) {
return MTK; return MTK;
} else if (MATCH(DTB_MAGIC)) { } else if (MATCH(DTB_MAGIC)) {
return DTB; return DTB;
} else if (MATCH(DHTB_MAGIC)) { } else if (MATCH(DHTB_MAGIC)) {
return DHTB; return DHTB;
} else if (MATCH(TEGRABLOB_MAGIC)) { } else if (MATCH(TEGRABLOB_MAGIC)) {
return BLOB; return BLOB;
} else { } else {
return UNKNOWN; return UNKNOWN;
} }
} }
const char *Fmt2Name::operator[](format_t fmt) { const char *Fmt2Name::operator[](format_t fmt) {
switch (fmt) { switch (fmt) {
case GZIP: case GZIP:
return "gzip"; return "gzip";
case LZOP: case LZOP:
return "lzop"; return "lzop";
case XZ: case XZ:
return "xz"; return "xz";
case LZMA: case LZMA:
return "lzma"; return "lzma";
case BZIP2: case BZIP2:
return "bzip2"; return "bzip2";
case LZ4: case LZ4:
return "lz4"; return "lz4";
case LZ4_LEGACY: case LZ4_LEGACY:
return "lz4_legacy"; return "lz4_legacy";
case LZ4_LG: case LZ4_LG:
return "lz4_lg"; return "lz4_lg";
case DTB: case DTB:
return "dtb"; return "dtb";
default: default:
return "raw"; return "raw";
} }
} }
const char *Fmt2Ext::operator[](format_t fmt) { const char *Fmt2Ext::operator[](format_t fmt) {
switch (fmt) { switch (fmt) {
case GZIP: case GZIP:
return ".gz"; return ".gz";
case LZOP: case LZOP:
return ".lzo"; return ".lzo";
case XZ: case XZ:
return ".xz"; return ".xz";
case LZMA: case LZMA:
return ".lzma"; return ".lzma";
case BZIP2: case BZIP2:
return ".bz2"; return ".bz2";
case LZ4: case LZ4:
case LZ4_LEGACY: case LZ4_LEGACY:
case LZ4_LG: case LZ4_LG:
return ".lz4"; return ".lz4";
default: default:
return ""; return "";
} }
} }

View File

@@ -4,26 +4,26 @@
#include <string_view> #include <string_view>
typedef enum { typedef enum {
UNKNOWN, UNKNOWN,
/* Boot formats */ /* Boot formats */
CHROMEOS, CHROMEOS,
AOSP, AOSP,
AOSP_VENDOR, AOSP_VENDOR,
DHTB, DHTB,
BLOB, BLOB,
/* Compression formats */ /* Compression formats */
GZIP, GZIP,
XZ, XZ,
LZMA, LZMA,
BZIP2, BZIP2,
LZ4, LZ4,
LZ4_LEGACY, LZ4_LEGACY,
LZ4_LG, LZ4_LG,
/* Unsupported compression */ /* Unsupported compression */
LZOP, LZOP,
/* Misc */ /* Misc */
MTK, MTK,
DTB, DTB,
} format_t; } format_t;
#define COMPRESSED(fmt) ((fmt) >= GZIP && (fmt) < LZOP) #define COMPRESSED(fmt) ((fmt) >= GZIP && (fmt) < LZOP)
@@ -57,12 +57,12 @@ typedef enum {
class Fmt2Name { class Fmt2Name {
public: public:
const char *operator[](format_t fmt); const char *operator[](format_t fmt);
}; };
class Fmt2Ext { class Fmt2Ext {
public: public:
const char *operator[](format_t fmt); const char *operator[](format_t fmt);
}; };
format_t check_fmt(const void *buf, size_t len); format_t check_fmt(const void *buf, size_t len);

View File

@@ -8,36 +8,36 @@
#include "magiskboot.hpp" #include "magiskboot.hpp"
static void hex2byte(uint8_t *hex, uint8_t *str) { static void hex2byte(uint8_t *hex, uint8_t *str) {
char high, low; char high, low;
for (int i = 0, length = strlen((char *) hex); i < length; i += 2) { for (int i = 0, length = strlen((char *) hex); i < length; i += 2) {
high = toupper(hex[i]) - '0'; high = toupper(hex[i]) - '0';
low = toupper(hex[i + 1]) - '0'; low = toupper(hex[i + 1]) - '0';
str[i / 2] = ((high > 9 ? high - 7 : high) << 4) + (low > 9 ? low - 7 : low); str[i / 2] = ((high > 9 ? high - 7 : high) << 4) + (low > 9 ? low - 7 : low);
} }
} }
int hexpatch(const char *image, const char *from, const char *to) { int hexpatch(const char *image, const char *from, const char *to) {
int patternsize = strlen(from) / 2, patchsize = strlen(to) / 2; int patternsize = strlen(from) / 2, patchsize = strlen(to) / 2;
int patched = 1; int patched = 1;
size_t filesize; size_t filesize;
uint8_t *file, *pattern, *patch; uint8_t *file, *pattern, *patch;
mmap_rw(image, file, filesize); mmap_rw(image, file, filesize);
pattern = (uint8_t *) xmalloc(patternsize); pattern = (uint8_t *) xmalloc(patternsize);
patch = (uint8_t *) xmalloc(patchsize); patch = (uint8_t *) xmalloc(patchsize);
hex2byte((uint8_t *) from, pattern); hex2byte((uint8_t *) from, pattern);
hex2byte((uint8_t *) to, patch); hex2byte((uint8_t *) to, patch);
for (size_t i = 0; filesize > 0 && i < filesize - patternsize; ++i) { for (size_t i = 0; filesize > 0 && i < filesize - patternsize; ++i) {
if (memcmp(file + i, pattern, patternsize) == 0) { if (memcmp(file + i, pattern, patternsize) == 0) {
fprintf(stderr, "Patch @ %08X [%s]->[%s]\n", (unsigned) i, from, to); fprintf(stderr, "Patch @ %08X [%s]->[%s]\n", (unsigned) i, from, to);
memset(file + i, 0, patternsize); memset(file + i, 0, patternsize);
memcpy(file + i, patch, patchsize); memcpy(file + i, patch, patchsize);
i += patternsize - 1; i += patternsize - 1;
patched = 0; patched = 0;
} }
} }
munmap(file, filesize); munmap(file, filesize);
free(pattern); free(pattern);
free(patch); free(patch);
return patched; return patched;
} }

View File

@@ -13,7 +13,7 @@
using namespace std; using namespace std;
static void usage(char *arg0) { static void usage(char *arg0) {
fprintf(stderr, fprintf(stderr,
R"EOF(MagiskBoot - Boot Image Modification Tool R"EOF(MagiskBoot - Boot Image Modification Tool
Usage: %s <action> [args...] Usage: %s <action> [args...]
@@ -96,100 +96,100 @@ Supported actions:
<infile>/[outfile] can be '-' to be STDIN/STDOUT <infile>/[outfile] can be '-' to be STDIN/STDOUT
Supported methods: )EOF", arg0); Supported methods: )EOF", arg0);
for (auto &it : name2fmt) for (auto &it : name2fmt)
fprintf(stderr, "%s ", it.first.data()); fprintf(stderr, "%s ", it.first.data());
fprintf(stderr, R"EOF( fprintf(stderr, R"EOF(
decompress <infile> [outfile] decompress <infile> [outfile]
Detect method and decompress <infile>, optionally to [outfile] Detect method and decompress <infile>, optionally to [outfile]
<infile>/[outfile] can be '-' to be STDIN/STDOUT <infile>/[outfile] can be '-' to be STDIN/STDOUT
Supported methods: )EOF"); Supported methods: )EOF");
for (auto &it : name2fmt) for (auto &it : name2fmt)
fprintf(stderr, "%s ", it.first.data()); fprintf(stderr, "%s ", it.first.data());
fprintf(stderr, "\n\n"); fprintf(stderr, "\n\n");
exit(1); exit(1);
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
cmdline_logging(); cmdline_logging();
umask(0); umask(0);
if (argc < 2) if (argc < 2)
usage(argv[0]); usage(argv[0]);
// Skip '--' for backwards compatibility // Skip '--' for backwards compatibility
string_view action(argv[1]); string_view action(argv[1]);
if (str_starts(action, "--")) if (str_starts(action, "--"))
action = argv[1] + 2; action = argv[1] + 2;
if (action == "cleanup") { if (action == "cleanup") {
fprintf(stderr, "Cleaning up...\n"); fprintf(stderr, "Cleaning up...\n");
unlink(HEADER_FILE); unlink(HEADER_FILE);
unlink(KERNEL_FILE); unlink(KERNEL_FILE);
unlink(RAMDISK_FILE); unlink(RAMDISK_FILE);
unlink(SECOND_FILE); unlink(SECOND_FILE);
unlink(KER_DTB_FILE); unlink(KER_DTB_FILE);
unlink(EXTRA_FILE); unlink(EXTRA_FILE);
unlink(RECV_DTBO_FILE); unlink(RECV_DTBO_FILE);
unlink(DTB_FILE); unlink(DTB_FILE);
} else if (argc > 2 && action == "sha1") { } else if (argc > 2 && action == "sha1") {
uint8_t sha1[SHA_DIGEST_SIZE]; uint8_t sha1[SHA_DIGEST_SIZE];
void *buf; void *buf;
size_t size; size_t size;
mmap_ro(argv[2], buf, size); mmap_ro(argv[2], buf, size);
SHA_hash(buf, size, sha1); SHA_hash(buf, size, sha1);
for (uint8_t i : sha1) for (uint8_t i : sha1)
printf("%02x", i); printf("%02x", i);
printf("\n"); printf("\n");
munmap(buf, size); munmap(buf, size);
} else if (argc > 2 && action == "split") { } else if (argc > 2 && action == "split") {
return split_image_dtb(argv[2]); return split_image_dtb(argv[2]);
} else if (argc > 2 && action == "unpack") { } else if (argc > 2 && action == "unpack") {
int idx = 2; int idx = 2;
bool nodecomp = false; bool nodecomp = false;
bool hdr = false; bool hdr = false;
for (;;) { for (;;) {
if (idx >= argc) if (idx >= argc)
usage(argv[0]); usage(argv[0]);
if (argv[idx][0] != '-') if (argv[idx][0] != '-')
break; break;
for (char *flag = &argv[idx][1]; *flag; ++flag) { for (char *flag = &argv[idx][1]; *flag; ++flag) {
if (*flag == 'n') if (*flag == 'n')
nodecomp = true; nodecomp = true;
else if (*flag == 'h') else if (*flag == 'h')
hdr = true; hdr = true;
else else
usage(argv[0]); usage(argv[0]);
} }
++idx; ++idx;
} }
return unpack(argv[idx], nodecomp, hdr); return unpack(argv[idx], nodecomp, hdr);
} else if (argc > 2 && action == "repack") { } else if (argc > 2 && action == "repack") {
if (argv[2] == "-n"sv) { if (argv[2] == "-n"sv) {
if (argc == 3) if (argc == 3)
usage(argv[0]); usage(argv[0]);
repack(argv[3], argv[4] ? argv[4] : NEW_BOOT, true); repack(argv[3], argv[4] ? argv[4] : NEW_BOOT, true);
} else { } else {
repack(argv[2], argv[3] ? argv[3] : NEW_BOOT); repack(argv[2], argv[3] ? argv[3] : NEW_BOOT);
} }
} else if (argc > 2 && action == "decompress") { } else if (argc > 2 && action == "decompress") {
decompress(argv[2], argv[3]); decompress(argv[2], argv[3]);
} else if (argc > 2 && str_starts(action, "compress")) { } else if (argc > 2 && str_starts(action, "compress")) {
compress(action[8] == '=' ? &action[9] : "gzip", argv[2], argv[3]); compress(action[8] == '=' ? &action[9] : "gzip", argv[2], argv[3]);
} else if (argc > 4 && action == "hexpatch") { } else if (argc > 4 && action == "hexpatch") {
return hexpatch(argv[2], argv[3], argv[4]); return hexpatch(argv[2], argv[3], argv[4]);
} else if (argc > 2 && action == "cpio"sv) { } else if (argc > 2 && action == "cpio"sv) {
if (cpio_commands(argc - 2, argv + 2)) if (cpio_commands(argc - 2, argv + 2))
usage(argv[0]); usage(argv[0]);
} else if (argc > 3 && action == "dtb") { } else if (argc > 3 && action == "dtb") {
if (dtb_commands(argc - 2, argv + 2)) if (dtb_commands(argc - 2, argv + 2))
usage(argv[0]); usage(argv[0]);
} else { } else {
usage(argv[0]); usage(argv[0]);
} }
return 0; return 0;
} }

View File

@@ -8,56 +8,56 @@
#define MATCH(p) else if (strncmp(s + skip, p, sizeof(p) - 1) == 0) skip += (sizeof(p) - 1) #define MATCH(p) else if (strncmp(s + skip, p, sizeof(p) - 1) == 0) skip += (sizeof(p) - 1)
static int check_verity_pattern(const char *s) { static int check_verity_pattern(const char *s) {
int skip = s[0] == ','; int skip = s[0] == ',';
if (0) {} if (0) {}
MATCH("verifyatboot"); MATCH("verifyatboot");
MATCH("verify"); MATCH("verify");
MATCH("avb_keys"); MATCH("avb_keys");
MATCH("avb"); MATCH("avb");
MATCH("support_scfs"); MATCH("support_scfs");
MATCH("fsverity"); MATCH("fsverity");
else return -1; else return -1;
if (s[skip] == '=') { if (s[skip] == '=') {
while (s[skip] != '\0' && s[skip] != ' ' && s[skip] != '\n' && s[skip] != ',') while (s[skip] != '\0' && s[skip] != ' ' && s[skip] != '\n' && s[skip] != ',')
++skip; ++skip;
} }
return skip; return skip;
} }
#undef MATCH #undef MATCH
#define MATCH(p) else if (strncmp(s, p, sizeof(p) - 1) == 0) return (sizeof(p) - 1) #define MATCH(p) else if (strncmp(s, p, sizeof(p) - 1) == 0) return (sizeof(p) - 1)
static int check_encryption_pattern(const char *s) { static int check_encryption_pattern(const char *s) {
if (0) {} if (0) {}
MATCH("forceencrypt"); MATCH("forceencrypt");
MATCH("forcefdeorfbe"); MATCH("forcefdeorfbe");
MATCH("fileencryption"); MATCH("fileencryption");
else return -1; else return -1;
} }
static uint32_t remove_pattern(void *buf, uint32_t size, int(*pattern_skip)(const char *)) { static uint32_t remove_pattern(void *buf, uint32_t size, int(*pattern_skip)(const char *)) {
auto src = static_cast<char *>(buf); auto src = static_cast<char *>(buf);
int orig_sz = size; int orig_sz = size;
int write = 0; int write = 0;
for (int read = 0; read < orig_sz;) { for (int read = 0; read < orig_sz;) {
if (int skip = pattern_skip(src + read); skip > 0) { if (int skip = pattern_skip(src + read); skip > 0) {
fprintf(stderr, "Remove pattern [%.*s]\n", skip, src + read); fprintf(stderr, "Remove pattern [%.*s]\n", skip, src + read);
size -= skip; size -= skip;
read += skip; read += skip;
} else { } else {
src[write++] = src[read++]; src[write++] = src[read++];
} }
} }
memset(src + write, 0, orig_sz - write); memset(src + write, 0, orig_sz - write);
return size; return size;
} }
uint32_t patch_verity(void *buf, uint32_t size) { uint32_t patch_verity(void *buf, uint32_t size) {
return remove_pattern(buf, size, check_verity_pattern); return remove_pattern(buf, size, check_verity_pattern);
} }
uint32_t patch_encryption(void *buf, uint32_t size) { uint32_t patch_encryption(void *buf, uint32_t size) {
return remove_pattern(buf, size, check_encryption_pattern); return remove_pattern(buf, size, check_encryption_pattern);
} }

View File

@@ -13,60 +13,60 @@ using namespace std;
constexpr char RAMDISK_XZ[] = "ramdisk.cpio.xz"; constexpr char RAMDISK_XZ[] = "ramdisk.cpio.xz";
static const char *UNSUPPORT_LIST[] = static const char *UNSUPPORT_LIST[] =
{ "sbin/launch_daemonsu.sh", "sbin/su", "init.xposed.rc", { "sbin/launch_daemonsu.sh", "sbin/su", "init.xposed.rc",
"boot/sbin/launch_daemonsu.sh" }; "boot/sbin/launch_daemonsu.sh" };
static const char *MAGISK_LIST[] = static const char *MAGISK_LIST[] =
{ ".backup/.magisk", "init.magisk.rc", { ".backup/.magisk", "init.magisk.rc",
"overlay/init.magisk.rc" }; "overlay/init.magisk.rc" };
class magisk_cpio : public cpio_rw { class magisk_cpio : public cpio_rw {
public: public:
magisk_cpio() = default; magisk_cpio() = default;
explicit magisk_cpio(const char *filename) : cpio_rw(filename) {} explicit magisk_cpio(const char *filename) : cpio_rw(filename) {}
void patch(); void patch();
int test(); int test();
char *sha1(); char *sha1();
void restore(); void restore();
void backup(const char *orig); void backup(const char *orig);
void compress(); void compress();
void decompress(); void decompress();
}; };
bool check_env(const char *name) { bool check_env(const char *name) {
const char *val = getenv(name); const char *val = getenv(name);
return val ? strcmp(val, "true") == 0 : false; return val ? strcmp(val, "true") == 0 : false;
} }
void magisk_cpio::patch() { void magisk_cpio::patch() {
bool keepverity = check_env("KEEPVERITY"); bool keepverity = check_env("KEEPVERITY");
bool keepforceencrypt = check_env("KEEPFORCEENCRYPT"); bool keepforceencrypt = check_env("KEEPFORCEENCRYPT");
fprintf(stderr, "Patch with flag KEEPVERITY=[%s] KEEPFORCEENCRYPT=[%s]\n", fprintf(stderr, "Patch with flag KEEPVERITY=[%s] KEEPFORCEENCRYPT=[%s]\n",
keepverity ? "true" : "false", keepforceencrypt ? "true" : "false"); keepverity ? "true" : "false", keepforceencrypt ? "true" : "false");
for (auto it = entries.begin(); it != entries.end();) { for (auto it = entries.begin(); it != entries.end();) {
auto cur = it++; auto cur = it++;
bool fstab = (!keepverity || !keepforceencrypt) && bool fstab = (!keepverity || !keepforceencrypt) &&
S_ISREG(cur->second->mode) && S_ISREG(cur->second->mode) &&
!str_starts(cur->first, ".backup") && !str_starts(cur->first, ".backup") &&
!str_contains(cur->first, "twrp") && !str_contains(cur->first, "twrp") &&
!str_contains(cur->first, "recovery") && !str_contains(cur->first, "recovery") &&
str_contains(cur->first, "fstab"); str_contains(cur->first, "fstab");
if (!keepverity) { if (!keepverity) {
if (fstab) { if (fstab) {
fprintf(stderr, "Found fstab file [%s]\n", cur->first.data()); fprintf(stderr, "Found fstab file [%s]\n", cur->first.data());
cur->second->filesize = patch_verity(cur->second->data, cur->second->filesize); cur->second->filesize = patch_verity(cur->second->data, cur->second->filesize);
} else if (cur->first == "verity_key") { } else if (cur->first == "verity_key") {
rm(cur); rm(cur);
continue; continue;
} }
} }
if (!keepforceencrypt) { if (!keepforceencrypt) {
if (fstab) { if (fstab) {
cur->second->filesize = patch_encryption(cur->second->data, cur->second->filesize); cur->second->filesize = patch_encryption(cur->second->data, cur->second->filesize);
} }
} }
} }
} }
#define STOCK_BOOT 0 #define STOCK_BOOT 0
@@ -76,274 +76,274 @@ void magisk_cpio::patch() {
#define TWO_STAGE_INIT (1 << 3) #define TWO_STAGE_INIT (1 << 3)
int magisk_cpio::test() { int magisk_cpio::test() {
for (auto file : UNSUPPORT_LIST) for (auto file : UNSUPPORT_LIST)
if (exists(file)) if (exists(file))
return UNSUPPORTED_CPIO; return UNSUPPORTED_CPIO;
int flags = STOCK_BOOT; int flags = STOCK_BOOT;
if (exists(RAMDISK_XZ)) { if (exists(RAMDISK_XZ)) {
flags |= COMPRESSED_CPIO | MAGISK_PATCHED; flags |= COMPRESSED_CPIO | MAGISK_PATCHED;
decompress(); decompress();
} }
if (exists("apex") || exists("first_stage_ramdisk")) if (exists("apex") || exists("first_stage_ramdisk"))
flags |= TWO_STAGE_INIT; flags |= TWO_STAGE_INIT;
for (auto file : MAGISK_LIST) { for (auto file : MAGISK_LIST) {
if (exists(file)) { if (exists(file)) {
flags |= MAGISK_PATCHED; flags |= MAGISK_PATCHED;
break; break;
} }
} }
return flags; return flags;
} }
#define for_each_line(line, buf, size) \ #define for_each_line(line, buf, size) \
for (line = (char *) buf; line < (char *) buf + size && line[0]; line = strchr(line + 1, '\n') + 1) for (line = (char *) buf; line < (char *) buf + size && line[0]; line = strchr(line + 1, '\n') + 1)
char *magisk_cpio::sha1() { char *magisk_cpio::sha1() {
decompress(); decompress();
char sha1[41]; char sha1[41];
char *line; char *line;
for (auto &e : entries) { for (auto &e : entries) {
if (e.first == "init.magisk.rc" || e.first == "overlay/init.magisk.rc") { if (e.first == "init.magisk.rc" || e.first == "overlay/init.magisk.rc") {
for_each_line(line, e.second->data, e.second->filesize) { for_each_line(line, e.second->data, e.second->filesize) {
if (strncmp(line, "#STOCKSHA1=", 11) == 0) { if (strncmp(line, "#STOCKSHA1=", 11) == 0) {
strncpy(sha1, line + 12, 40); strncpy(sha1, line + 12, 40);
sha1[40] = '\0'; sha1[40] = '\0';
return strdup(sha1); return strdup(sha1);
} }
} }
} else if (e.first == ".backup/.magisk") { } else if (e.first == ".backup/.magisk") {
for_each_line(line, e.second->data, e.second->filesize) { for_each_line(line, e.second->data, e.second->filesize) {
if (strncmp(line, "SHA1=", 5) == 0) { if (strncmp(line, "SHA1=", 5) == 0) {
strncpy(sha1, line + 5, 40); strncpy(sha1, line + 5, 40);
sha1[40] = '\0'; sha1[40] = '\0';
return strdup(sha1); return strdup(sha1);
} }
} }
} else if (e.first == ".backup/.sha1") { } else if (e.first == ".backup/.sha1") {
return (char *) e.second->data; return (char *) e.second->data;
} }
} }
return nullptr; return nullptr;
} }
#define for_each_str(str, buf, size) \ #define for_each_str(str, buf, size) \
for (str = (char *) buf; str < (char *) buf + size; str = str += strlen(str) + 1) for (str = (char *) buf; str < (char *) buf + size; str = str += strlen(str) + 1)
void magisk_cpio::restore() { void magisk_cpio::restore() {
decompress(); decompress();
if (auto it = entries.find(".backup/.rmlist"); it != entries.end()) { if (auto it = entries.find(".backup/.rmlist"); it != entries.end()) {
char *file; char *file;
for_each_str(file, it->second->data, it->second->filesize) for_each_str(file, it->second->data, it->second->filesize)
rm(file, false); rm(file, false);
rm(it); rm(it);
} }
for (auto it = entries.begin(); it != entries.end();) { for (auto it = entries.begin(); it != entries.end();) {
auto cur = it++; auto cur = it++;
if (str_starts(cur->first, ".backup")) { if (str_starts(cur->first, ".backup")) {
if (cur->first.length() == 7 || cur->first.substr(8) == ".magisk") { if (cur->first.length() == 7 || cur->first.substr(8) == ".magisk") {
rm(cur); rm(cur);
} else { } else {
mv(cur, &cur->first[8]); mv(cur, &cur->first[8]);
} }
} else if (str_starts(cur->first, "magisk") || } else if (str_starts(cur->first, "magisk") ||
cur->first == "overlay/init.magisk.rc" || cur->first == "overlay/init.magisk.rc" ||
cur->first == "sbin/magic_mask.sh" || cur->first == "sbin/magic_mask.sh" ||
cur->first == "init.magisk.rc") { cur->first == "init.magisk.rc") {
// Some known stuff we can remove // Some known stuff we can remove
rm(cur); rm(cur);
} }
} }
} }
void magisk_cpio::backup(const char *orig) { void magisk_cpio::backup(const char *orig) {
if (access(orig, R_OK)) if (access(orig, R_OK))
return; return;
entry_map bkup_entries; entry_map bkup_entries;
string remv; string remv;
auto b = new cpio_entry(".backup", S_IFDIR); auto b = new cpio_entry(".backup", S_IFDIR);
bkup_entries[b->filename].reset(b); bkup_entries[b->filename].reset(b);
magisk_cpio o(orig); magisk_cpio o(orig);
// Remove possible backups in original ramdisk // Remove possible backups in original ramdisk
o.rm(".backup", true); o.rm(".backup", true);
rm(".backup", true); rm(".backup", true);
auto lhs = o.entries.begin(); auto lhs = o.entries.begin();
auto rhs = entries.begin(); auto rhs = entries.begin();
while (lhs != o.entries.end() || rhs != entries.end()) { while (lhs != o.entries.end() || rhs != entries.end()) {
int res; int res;
bool backup = false; bool backup = false;
if (lhs != o.entries.end() && rhs != entries.end()) { if (lhs != o.entries.end() && rhs != entries.end()) {
res = lhs->first.compare(rhs->first); res = lhs->first.compare(rhs->first);
} else if (lhs == o.entries.end()) { } else if (lhs == o.entries.end()) {
res = 1; res = 1;
} else { } else {
res = -1; res = -1;
} }
if (res < 0) { if (res < 0) {
// Something is missing in new ramdisk, backup! // Something is missing in new ramdisk, backup!
backup = true; backup = true;
fprintf(stderr, "Backup missing entry: "); fprintf(stderr, "Backup missing entry: ");
} else if (res == 0) { } else if (res == 0) {
if (lhs->second->filesize != rhs->second->filesize || if (lhs->second->filesize != rhs->second->filesize ||
memcmp(lhs->second->data, rhs->second->data, lhs->second->filesize) != 0) { memcmp(lhs->second->data, rhs->second->data, lhs->second->filesize) != 0) {
// Not the same! // Not the same!
backup = true; backup = true;
fprintf(stderr, "Backup mismatch entry: "); fprintf(stderr, "Backup mismatch entry: ");
} }
} else { } else {
// Something new in ramdisk // Something new in ramdisk
remv += rhs->first; remv += rhs->first;
remv += (char) '\0'; remv += (char) '\0';
fprintf(stderr, "Record new entry: [%s] -> [.backup/.rmlist]\n", rhs->first.data()); fprintf(stderr, "Record new entry: [%s] -> [.backup/.rmlist]\n", rhs->first.data());
} }
if (backup) { if (backup) {
string back_name(".backup/"); string back_name(".backup/");
back_name += lhs->first; back_name += lhs->first;
fprintf(stderr, "[%s] -> [%s]\n", lhs->first.data(), back_name.data()); fprintf(stderr, "[%s] -> [%s]\n", lhs->first.data(), back_name.data());
auto ex = static_cast<cpio_entry*>(lhs->second.release()); auto ex = static_cast<cpio_entry*>(lhs->second.release());
ex->filename = back_name; ex->filename = back_name;
bkup_entries[ex->filename].reset(ex); bkup_entries[ex->filename].reset(ex);
} }
// Increment positions // Increment positions
if (res < 0) { if (res < 0) {
++lhs; ++lhs;
} else if (res == 0) { } else if (res == 0) {
++lhs; ++rhs; ++lhs; ++rhs;
} else { } else {
++rhs; ++rhs;
} }
} }
if (!remv.empty()) { if (!remv.empty()) {
auto rmlist = new cpio_entry(".backup/.rmlist", S_IFREG); auto rmlist = new cpio_entry(".backup/.rmlist", S_IFREG);
rmlist->filesize = remv.length(); rmlist->filesize = remv.length();
rmlist->data = xmalloc(remv.length()); rmlist->data = xmalloc(remv.length());
memcpy(rmlist->data, remv.data(), remv.length()); memcpy(rmlist->data, remv.data(), remv.length());
bkup_entries[rmlist->filename].reset(rmlist); bkup_entries[rmlist->filename].reset(rmlist);
} }
if (bkup_entries.size() > 1) if (bkup_entries.size() > 1)
entries.merge(bkup_entries); entries.merge(bkup_entries);
} }
void magisk_cpio::compress() { void magisk_cpio::compress() {
if (exists(RAMDISK_XZ)) if (exists(RAMDISK_XZ))
return; return;
fprintf(stderr, "Compressing cpio -> [%s]\n", RAMDISK_XZ); fprintf(stderr, "Compressing cpio -> [%s]\n", RAMDISK_XZ);
auto init = entries.extract("init"); auto init = entries.extract("init");
uint8_t *data; uint8_t *data;
size_t len; size_t len;
auto strm = make_stream_fp(get_encoder(XZ, make_unique<byte_stream>(data, len))); auto strm = make_stream_fp(get_encoder(XZ, make_unique<byte_stream>(data, len)));
dump(strm.release()); dump(strm.release());
entries.clear(); entries.clear();
entries.insert(std::move(init)); entries.insert(std::move(init));
auto xz = new cpio_entry(RAMDISK_XZ, S_IFREG); auto xz = new cpio_entry(RAMDISK_XZ, S_IFREG);
xz->data = data; xz->data = data;
xz->filesize = len; xz->filesize = len;
insert(xz); insert(xz);
} }
void magisk_cpio::decompress() { void magisk_cpio::decompress() {
auto it = entries.find(RAMDISK_XZ); auto it = entries.find(RAMDISK_XZ);
if (it == entries.end()) if (it == entries.end())
return; return;
fprintf(stderr, "Decompressing cpio [%s]\n", RAMDISK_XZ); fprintf(stderr, "Decompressing cpio [%s]\n", RAMDISK_XZ);
char *data; char *data;
size_t len; size_t len;
{ {
auto strm = get_decoder(XZ, make_unique<byte_stream>(data, len)); auto strm = get_decoder(XZ, make_unique<byte_stream>(data, len));
strm->write(it->second->data, it->second->filesize); strm->write(it->second->data, it->second->filesize);
} }
entries.erase(it); entries.erase(it);
load_cpio(data, len); load_cpio(data, len);
free(data); free(data);
} }
int cpio_commands(int argc, char *argv[]) { int cpio_commands(int argc, char *argv[]) {
char *incpio = argv[0]; char *incpio = argv[0];
++argv; ++argv;
--argc; --argc;
magisk_cpio cpio; magisk_cpio cpio;
if (access(incpio, R_OK) == 0) if (access(incpio, R_OK) == 0)
cpio.load_cpio(incpio); cpio.load_cpio(incpio);
int cmdc; int cmdc;
char *cmdv[6]; char *cmdv[6];
while (argc) { while (argc) {
// Clean up // Clean up
cmdc = 0; cmdc = 0;
memset(cmdv, NULL, sizeof(cmdv)); memset(cmdv, NULL, sizeof(cmdv));
// Split the commands // Split the commands
for (char *tok = strtok(argv[0], " "); tok; tok = strtok(nullptr, " ")) for (char *tok = strtok(argv[0], " "); tok; tok = strtok(nullptr, " "))
cmdv[cmdc++] = tok; cmdv[cmdc++] = tok;
if (cmdc == 0) if (cmdc == 0)
continue; continue;
if (strcmp(cmdv[0], "test") == 0) { if (strcmp(cmdv[0], "test") == 0) {
exit(cpio.test()); exit(cpio.test());
} else if (strcmp(cmdv[0], "restore") == 0) { } else if (strcmp(cmdv[0], "restore") == 0) {
cpio.restore(); cpio.restore();
} else if (strcmp(cmdv[0], "sha1") == 0) { } else if (strcmp(cmdv[0], "sha1") == 0) {
char *sha1 = cpio.sha1(); char *sha1 = cpio.sha1();
if (sha1) printf("%s\n", sha1); if (sha1) printf("%s\n", sha1);
return 0; return 0;
} else if (strcmp(cmdv[0], "compress") == 0){ } else if (strcmp(cmdv[0], "compress") == 0){
cpio.compress(); cpio.compress();
} else if (strcmp(cmdv[0], "decompress") == 0){ } else if (strcmp(cmdv[0], "decompress") == 0){
cpio.decompress(); cpio.decompress();
} else if (strcmp(cmdv[0], "patch") == 0) { } else if (strcmp(cmdv[0], "patch") == 0) {
cpio.patch(); cpio.patch();
} else if (cmdc == 2 && strcmp(cmdv[0], "exists") == 0) { } else if (cmdc == 2 && strcmp(cmdv[0], "exists") == 0) {
exit(!cpio.exists(cmdv[1])); exit(!cpio.exists(cmdv[1]));
} else if (cmdc == 2 && strcmp(cmdv[0], "backup") == 0) { } else if (cmdc == 2 && strcmp(cmdv[0], "backup") == 0) {
cpio.backup(cmdv[1]); cpio.backup(cmdv[1]);
} else if (cmdc >= 2 && strcmp(cmdv[0], "rm") == 0) { } else if (cmdc >= 2 && strcmp(cmdv[0], "rm") == 0) {
bool r = cmdc > 2 && strcmp(cmdv[1], "-r") == 0; bool r = cmdc > 2 && strcmp(cmdv[1], "-r") == 0;
cpio.rm(cmdv[1 + r], r); cpio.rm(cmdv[1 + r], r);
} else if (cmdc == 3 && strcmp(cmdv[0], "mv") == 0) { } else if (cmdc == 3 && strcmp(cmdv[0], "mv") == 0) {
cpio.mv(cmdv[1], cmdv[2]); cpio.mv(cmdv[1], cmdv[2]);
} else if (strcmp(cmdv[0], "extract") == 0) { } else if (strcmp(cmdv[0], "extract") == 0) {
if (cmdc == 3) { if (cmdc == 3) {
return !cpio.extract(cmdv[1], cmdv[2]); return !cpio.extract(cmdv[1], cmdv[2]);
} else { } else {
cpio.extract(); cpio.extract();
return 0; return 0;
} }
} else if (cmdc == 3 && strcmp(cmdv[0], "mkdir") == 0) { } else if (cmdc == 3 && strcmp(cmdv[0], "mkdir") == 0) {
cpio.mkdir(strtoul(cmdv[1], nullptr, 8), cmdv[2]); cpio.mkdir(strtoul(cmdv[1], nullptr, 8), cmdv[2]);
} else if (cmdc == 3 && strcmp(cmdv[0], "ln") == 0) { } else if (cmdc == 3 && strcmp(cmdv[0], "ln") == 0) {
cpio.ln(cmdv[1], cmdv[2]); cpio.ln(cmdv[1], cmdv[2]);
} else if (cmdc == 4 && strcmp(cmdv[0], "add") == 0) { } else if (cmdc == 4 && strcmp(cmdv[0], "add") == 0) {
cpio.add(strtoul(cmdv[1], nullptr, 8), cmdv[2], cmdv[3]); cpio.add(strtoul(cmdv[1], nullptr, 8), cmdv[2], cmdv[3]);
} else { } else {
return 1; return 1;
} }
--argc; --argc;
++argv; ++argv;
} }
cpio.dump(incpio); cpio.dump(incpio);
return 0; return 0;
} }

View File

@@ -10,123 +10,123 @@
using namespace std; using namespace std;
static const char *prop_key[] = static const char *prop_key[] =
{ "ro.boot.vbmeta.device_state", "ro.boot.verifiedbootstate", "ro.boot.flash.locked", { "ro.boot.vbmeta.device_state", "ro.boot.verifiedbootstate", "ro.boot.flash.locked",
"ro.boot.veritymode", "ro.boot.warranty_bit", "ro.warranty_bit", "ro.boot.veritymode", "ro.boot.warranty_bit", "ro.warranty_bit",
"ro.debuggable", "ro.secure", "ro.build.type", "ro.build.tags", "ro.debuggable", "ro.secure", "ro.build.type", "ro.build.tags",
"ro.vendor.boot.warranty_bit", "ro.vendor.warranty_bit", "ro.vendor.boot.warranty_bit", "ro.vendor.warranty_bit",
"vendor.boot.vbmeta.device_state", nullptr }; "vendor.boot.vbmeta.device_state", nullptr };
static const char *prop_val[] = static const char *prop_val[] =
{ "locked", "green", "1", { "locked", "green", "1",
"enforcing", "0", "0", "enforcing", "0", "0",
"0", "1", "user", "release-keys", "0", "1", "user", "release-keys",
"0", "0", "0", "0",
"locked", nullptr }; "locked", nullptr };
static const char *late_prop_key[] = static const char *late_prop_key[] =
{ "vendor.boot.verifiedbootstate", nullptr }; { "vendor.boot.verifiedbootstate", nullptr };
static const char *late_prop_val[] = static const char *late_prop_val[] =
{ "green", nullptr }; { "green", nullptr };
void hide_sensitive_props() { void hide_sensitive_props() {
LOGI("hide_policy: Hiding sensitive props\n"); LOGI("hide_policy: Hiding sensitive props\n");
for (int i = 0; prop_key[i]; ++i) { for (int i = 0; prop_key[i]; ++i) {
auto value = getprop(prop_key[i]); auto value = getprop(prop_key[i]);
if (!value.empty() && value != prop_val[i]) if (!value.empty() && value != prop_val[i])
setprop(prop_key[i], prop_val[i], false); setprop(prop_key[i], prop_val[i], false);
} }
// Hide that we booted from recovery when magisk is in recovery mode // Hide that we booted from recovery when magisk is in recovery mode
auto bootmode = getprop("ro.bootmode"); auto bootmode = getprop("ro.bootmode");
if (!bootmode.empty() && str_contains(bootmode, "recovery")) if (!bootmode.empty() && str_contains(bootmode, "recovery"))
setprop("ro.bootmode", "unknown", false); setprop("ro.bootmode", "unknown", false);
bootmode = getprop("ro.boot.mode"); bootmode = getprop("ro.boot.mode");
if (!bootmode.empty() && str_contains(bootmode, "recovery")) if (!bootmode.empty() && str_contains(bootmode, "recovery"))
setprop("ro.boot.mode", "unknown", false); setprop("ro.boot.mode", "unknown", false);
bootmode = getprop("vendor.boot.mode"); bootmode = getprop("vendor.boot.mode");
if (!bootmode.empty() && str_contains(bootmode, "recovery")) if (!bootmode.empty() && str_contains(bootmode, "recovery"))
setprop("vendor.boot.mode", "unknown", false); setprop("vendor.boot.mode", "unknown", false);
// Xiaomi cross region flash // Xiaomi cross region flash
auto hwc = getprop("ro.boot.hwc"); auto hwc = getprop("ro.boot.hwc");
if (!hwc.empty() && str_contains(hwc, "CN")) if (!hwc.empty() && str_contains(hwc, "CN"))
setprop("ro.boot.hwc", "GLOBAL", false); setprop("ro.boot.hwc", "GLOBAL", false);
auto hwcountry = getprop("ro.boot.hwcountry"); auto hwcountry = getprop("ro.boot.hwcountry");
if (!hwcountry.empty() && str_contains(hwcountry, "China")) if (!hwcountry.empty() && str_contains(hwcountry, "China"))
setprop("ro.boot.hwcountry", "GLOBAL", false); setprop("ro.boot.hwcountry", "GLOBAL", false);
auto selinux = getprop("ro.build.selinux"); auto selinux = getprop("ro.build.selinux");
if (!selinux.empty()) if (!selinux.empty())
delprop("ro.build.selinux"); delprop("ro.build.selinux");
} }
void hide_late_sensitive_props() { void hide_late_sensitive_props() {
LOGI("hide_policy: Hiding sensitive props (late)\n"); LOGI("hide_policy: Hiding sensitive props (late)\n");
for (int i = 0; late_prop_key[i]; ++i) { for (int i = 0; late_prop_key[i]; ++i) {
auto value = getprop(late_prop_key[i]); auto value = getprop(late_prop_key[i]);
if (!value.empty() && value != late_prop_val[i]) if (!value.empty() && value != late_prop_val[i])
setprop(late_prop_key[i], late_prop_val[i], false); setprop(late_prop_key[i], late_prop_val[i], false);
} }
} }
static void lazy_unmount(const char* mountpoint) { static void lazy_unmount(const char* mountpoint) {
if (umount2(mountpoint, MNT_DETACH) != -1) if (umount2(mountpoint, MNT_DETACH) != -1)
LOGD("hide_policy: Unmounted (%s)\n", mountpoint); LOGD("hide_policy: Unmounted (%s)\n", mountpoint);
} }
void hide_daemon(int pid) { void hide_daemon(int pid) {
if (fork_dont_care() == 0) { if (fork_dont_care() == 0) {
hide_unmount(pid); hide_unmount(pid);
// Send resume signal // Send resume signal
kill(pid, SIGCONT); kill(pid, SIGCONT);
_exit(0); _exit(0);
} }
} }
#define TMPFS_MNT(dir) (mentry->mnt_type == "tmpfs"sv && \ #define TMPFS_MNT(dir) (mentry->mnt_type == "tmpfs"sv && \
strncmp(mentry->mnt_dir, "/" #dir, sizeof("/" #dir) - 1) == 0) strncmp(mentry->mnt_dir, "/" #dir, sizeof("/" #dir) - 1) == 0)
void hide_unmount(int pid) { void hide_unmount(int pid) {
if (switch_mnt_ns(pid)) if (switch_mnt_ns(pid))
return; return;
LOGD("hide_policy: handling PID=[%d]\n", pid); LOGD("hide_policy: handling PID=[%d]\n", pid);
char val; char val;
int fd = xopen(SELINUX_ENFORCE, O_RDONLY); int fd = xopen(SELINUX_ENFORCE, O_RDONLY);
xxread(fd, &val, sizeof(val)); xxread(fd, &val, sizeof(val));
close(fd); close(fd);
// Permissive // Permissive
if (val == '0') { if (val == '0') {
chmod(SELINUX_ENFORCE, 0640); chmod(SELINUX_ENFORCE, 0640);
chmod(SELINUX_POLICY, 0440); chmod(SELINUX_POLICY, 0440);
} }
vector<string> targets; vector<string> targets;
// Unmount dummy skeletons and /sbin links // Unmount dummy skeletons and /sbin links
targets.push_back(MAGISKTMP); targets.push_back(MAGISKTMP);
parse_mnt("/proc/self/mounts", [&](mntent *mentry) { parse_mnt("/proc/self/mounts", [&](mntent *mentry) {
if (TMPFS_MNT(system) || TMPFS_MNT(vendor) || TMPFS_MNT(product) || TMPFS_MNT(system_ext)) if (TMPFS_MNT(system) || TMPFS_MNT(vendor) || TMPFS_MNT(product) || TMPFS_MNT(system_ext))
targets.emplace_back(mentry->mnt_dir); targets.emplace_back(mentry->mnt_dir);
return true; return true;
}); });
for (auto &s : reversed(targets)) for (auto &s : reversed(targets))
lazy_unmount(s.data()); lazy_unmount(s.data());
targets.clear(); targets.clear();
// Unmount all Magisk created mounts // Unmount all Magisk created mounts
parse_mnt("/proc/self/mounts", [&](mntent *mentry) { parse_mnt("/proc/self/mounts", [&](mntent *mentry) {
if (strstr(mentry->mnt_fsname, BLOCKDIR)) if (strstr(mentry->mnt_fsname, BLOCKDIR))
targets.emplace_back(mentry->mnt_dir); targets.emplace_back(mentry->mnt_dir);
return true; return true;
}); });
for (auto &s : reversed(targets)) for (auto &s : reversed(targets))
lazy_unmount(s.data()); lazy_unmount(s.data());
} }

View File

@@ -24,169 +24,169 @@ static pthread_mutex_t hide_state_lock = PTHREAD_MUTEX_INITIALIZER;
// Leave /proc fd opened as we're going to read from it repeatedly // Leave /proc fd opened as we're going to read from it repeatedly
static DIR *procfp; static DIR *procfp;
void crawl_procfs(const function<bool(int)> &fn) { void crawl_procfs(const function<bool(int)> &fn) {
rewinddir(procfp); rewinddir(procfp);
crawl_procfs(procfp, fn); crawl_procfs(procfp, fn);
} }
void crawl_procfs(DIR *dir, const function<bool(int)> &fn) { void crawl_procfs(DIR *dir, const function<bool(int)> &fn) {
struct dirent *dp; struct dirent *dp;
int pid; int pid;
while ((dp = readdir(dir))) { while ((dp = readdir(dir))) {
pid = parse_int(dp->d_name); pid = parse_int(dp->d_name);
if (pid > 0 && !fn(pid)) if (pid > 0 && !fn(pid))
break; break;
} }
} }
bool hide_enabled() { bool hide_enabled() {
mutex_guard g(hide_state_lock); mutex_guard g(hide_state_lock);
return hide_state; return hide_state;
} }
void set_hide_state(bool state) { void set_hide_state(bool state) {
mutex_guard g(hide_state_lock); mutex_guard g(hide_state_lock);
hide_state = state; hide_state = state;
} }
template <bool str_op(string_view, string_view)> template <bool str_op(string_view, string_view)>
static bool proc_name_match(int pid, const char *name) { static bool proc_name_match(int pid, const char *name) {
char buf[4019]; char buf[4019];
sprintf(buf, "/proc/%d/cmdline", pid); sprintf(buf, "/proc/%d/cmdline", pid);
if (auto fp = open_file(buf, "re")) { if (auto fp = open_file(buf, "re")) {
fgets(buf, sizeof(buf), fp.get()); fgets(buf, sizeof(buf), fp.get());
if (str_op(buf, name)) { if (str_op(buf, name)) {
LOGD("hide_utils: kill PID=[%d] (%s)\n", pid, buf); LOGD("hide_utils: kill PID=[%d] (%s)\n", pid, buf);
return true; return true;
} }
} }
return false; return false;
} }
static inline bool str_eql(string_view s, string_view ss) { return s == ss; } static inline bool str_eql(string_view s, string_view ss) { return s == ss; }
static void kill_process(const char *name, bool multi = false, static void kill_process(const char *name, bool multi = false,
bool (*filter)(int, const char *) = proc_name_match<&str_eql>) { bool (*filter)(int, const char *) = proc_name_match<&str_eql>) {
crawl_procfs([=](int pid) -> bool { crawl_procfs([=](int pid) -> bool {
if (filter(pid, name)) { if (filter(pid, name)) {
kill(pid, SIGTERM); kill(pid, SIGTERM);
return multi; return multi;
} }
return true; return true;
}); });
} }
static bool validate(const char *s) { static bool validate(const char *s) {
if (strcmp(s, ISOLATED_MAGIC) == 0) if (strcmp(s, ISOLATED_MAGIC) == 0)
return true; return true;
bool dot = false; bool dot = false;
for (char c; (c = *s); ++s) { for (char c; (c = *s); ++s) {
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') || c == '_' || c == ':') { (c >= '0' && c <= '9') || c == '_' || c == ':') {
continue; continue;
} }
if (c == '.') { if (c == '.') {
dot = true; dot = true;
continue; continue;
} }
return false; return false;
} }
return dot; return dot;
} }
static void add_hide_set(const char *pkg, const char *proc) { static void add_hide_set(const char *pkg, const char *proc) {
LOGI("hide_list add: [%s/%s]\n", pkg, proc); LOGI("hide_list add: [%s/%s]\n", pkg, proc);
hide_set.emplace(pkg, proc); hide_set.emplace(pkg, proc);
if (strcmp(pkg, ISOLATED_MAGIC) == 0) { if (strcmp(pkg, ISOLATED_MAGIC) == 0) {
// Kill all matching isolated processes // Kill all matching isolated processes
kill_process(proc, true, proc_name_match<&str_starts>); kill_process(proc, true, proc_name_match<&str_starts>);
} else { } else {
kill_process(proc); kill_process(proc);
} }
} }
static int add_list(const char *pkg, const char *proc) { static int add_list(const char *pkg, const char *proc) {
if (proc[0] == '\0') if (proc[0] == '\0')
proc = pkg; proc = pkg;
if (!validate(pkg) || !validate(proc)) if (!validate(pkg) || !validate(proc))
return HIDE_INVALID_PKG; return HIDE_INVALID_PKG;
for (auto &hide : hide_set) for (auto &hide : hide_set)
if (hide.first == pkg && hide.second == proc) if (hide.first == pkg && hide.second == proc)
return HIDE_ITEM_EXIST; return HIDE_ITEM_EXIST;
// Add to database // Add to database
char sql[4096]; char sql[4096];
snprintf(sql, sizeof(sql), snprintf(sql, sizeof(sql),
"INSERT INTO hidelist (package_name, process) VALUES('%s', '%s')", pkg, proc); "INSERT INTO hidelist (package_name, process) VALUES('%s', '%s')", pkg, proc);
char *err = db_exec(sql); char *err = db_exec(sql);
db_err_cmd(err, return DAEMON_ERROR); db_err_cmd(err, return DAEMON_ERROR);
{ {
// Critical region // Critical region
mutex_guard lock(monitor_lock); mutex_guard lock(monitor_lock);
add_hide_set(pkg, proc); add_hide_set(pkg, proc);
} }
return DAEMON_SUCCESS; return DAEMON_SUCCESS;
} }
int add_list(int client) { int add_list(int client) {
char *pkg = read_string(client); char *pkg = read_string(client);
char *proc = read_string(client); char *proc = read_string(client);
int ret = add_list(pkg, proc); int ret = add_list(pkg, proc);
free(pkg); free(pkg);
free(proc); free(proc);
if (ret == DAEMON_SUCCESS) if (ret == DAEMON_SUCCESS)
update_uid_map(); update_uid_map();
return ret; return ret;
} }
static int rm_list(const char *pkg, const char *proc) { static int rm_list(const char *pkg, const char *proc) {
bool remove = false; bool remove = false;
{ {
// Critical region // Critical region
mutex_guard lock(monitor_lock); mutex_guard lock(monitor_lock);
for (auto it = hide_set.begin(); it != hide_set.end();) { for (auto it = hide_set.begin(); it != hide_set.end();) {
if (it->first == pkg && (proc[0] == '\0' || it->second == proc)) { if (it->first == pkg && (proc[0] == '\0' || it->second == proc)) {
remove = true; remove = true;
LOGI("hide_list rm: [%s/%s]\n", it->first.data(), it->second.data()); LOGI("hide_list rm: [%s/%s]\n", it->first.data(), it->second.data());
it = hide_set.erase(it); it = hide_set.erase(it);
} else { } else {
++it; ++it;
} }
} }
} }
if (!remove) if (!remove)
return HIDE_ITEM_NOT_EXIST; return HIDE_ITEM_NOT_EXIST;
char sql[4096]; char sql[4096];
if (proc[0] == '\0') if (proc[0] == '\0')
snprintf(sql, sizeof(sql), "DELETE FROM hidelist WHERE package_name='%s'", pkg); snprintf(sql, sizeof(sql), "DELETE FROM hidelist WHERE package_name='%s'", pkg);
else else
snprintf(sql, sizeof(sql), snprintf(sql, sizeof(sql),
"DELETE FROM hidelist WHERE package_name='%s' AND process='%s'", pkg, proc); "DELETE FROM hidelist WHERE package_name='%s' AND process='%s'", pkg, proc);
char *err = db_exec(sql); char *err = db_exec(sql);
db_err(err); db_err(err);
return DAEMON_SUCCESS; return DAEMON_SUCCESS;
} }
int rm_list(int client) { int rm_list(int client) {
char *pkg = read_string(client); char *pkg = read_string(client);
char *proc = read_string(client); char *proc = read_string(client);
int ret = rm_list(pkg, proc); int ret = rm_list(pkg, proc);
free(pkg); free(pkg);
free(proc); free(proc);
if (ret == DAEMON_SUCCESS) if (ret == DAEMON_SUCCESS)
update_uid_map(); update_uid_map();
return ret; return ret;
} }
static bool str_ends_safe(string_view s, string_view ss) { static bool str_ends_safe(string_view s, string_view ss) {
// Never kill webview zygote // Never kill webview zygote
if (s == "webview_zygote") if (s == "webview_zygote")
return false; return false;
return str_ends(s, ss); return str_ends(s, ss);
} }
#define SNET_PROC "com.google.android.gms.unstable" #define SNET_PROC "com.google.android.gms.unstable"
@@ -194,116 +194,116 @@ static bool str_ends_safe(string_view s, string_view ss) {
#define MICROG_PKG "org.microg.gms.droidguard" #define MICROG_PKG "org.microg.gms.droidguard"
static bool init_list() { static bool init_list() {
LOGD("hide_list: initialize\n"); LOGD("hide_list: initialize\n");
char *err = db_exec("SELECT * FROM hidelist", [](db_row &row) -> bool { char *err = db_exec("SELECT * FROM hidelist", [](db_row &row) -> bool {
add_hide_set(row["package_name"].data(), row["process"].data()); add_hide_set(row["package_name"].data(), row["process"].data());
return true; return true;
}); });
db_err_cmd(err, return false); db_err_cmd(err, return false);
// If Android Q+, also kill blastula pool and all app zygotes // If Android Q+, also kill blastula pool and all app zygotes
if (SDK_INT >= 29) { if (SDK_INT >= 29) {
kill_process("usap32", true); kill_process("usap32", true);
kill_process("usap64", true); kill_process("usap64", true);
kill_process("_zygote", true, proc_name_match<&str_ends_safe>); kill_process("_zygote", true, proc_name_match<&str_ends_safe>);
} }
// Add SafetyNet by default // Add SafetyNet by default
add_hide_set(GMS_PKG, SNET_PROC); add_hide_set(GMS_PKG, SNET_PROC);
add_hide_set(MICROG_PKG, SNET_PROC); add_hide_set(MICROG_PKG, SNET_PROC);
// We also need to hide the default GMS process if MAGISKTMP != /sbin // We also need to hide the default GMS process if MAGISKTMP != /sbin
// The snet process communicates with the main process and get additional info // The snet process communicates with the main process and get additional info
if (MAGISKTMP != "/sbin") if (MAGISKTMP != "/sbin")
add_hide_set(GMS_PKG, GMS_PKG); add_hide_set(GMS_PKG, GMS_PKG);
update_uid_map(); update_uid_map();
return true; return true;
} }
void ls_list(int client) { void ls_list(int client) {
FILE *out = fdopen(recv_fd(client), "a"); FILE *out = fdopen(recv_fd(client), "a");
for (auto &hide : hide_set) for (auto &hide : hide_set)
fprintf(out, "%s|%s\n", hide.first.data(), hide.second.data()); fprintf(out, "%s|%s\n", hide.first.data(), hide.second.data());
fclose(out); fclose(out);
write_int(client, DAEMON_SUCCESS); write_int(client, DAEMON_SUCCESS);
close(client); close(client);
} }
static void update_hide_config() { static void update_hide_config() {
char sql[64]; char sql[64];
sprintf(sql, "REPLACE INTO settings (key,value) VALUES('%s',%d)", sprintf(sql, "REPLACE INTO settings (key,value) VALUES('%s',%d)",
DB_SETTING_KEYS[HIDE_CONFIG], hide_state); DB_SETTING_KEYS[HIDE_CONFIG], hide_state);
char *err = db_exec(sql); char *err = db_exec(sql);
db_err(err); db_err(err);
} }
int launch_magiskhide() { int launch_magiskhide() {
mutex_guard g(hide_state_lock); mutex_guard g(hide_state_lock);
if (SDK_INT < 19) if (SDK_INT < 19)
return DAEMON_ERROR; return DAEMON_ERROR;
if (hide_state) if (hide_state)
return HIDE_IS_ENABLED; return HIDE_IS_ENABLED;
if (access("/proc/1/ns/mnt", F_OK) != 0) if (access("/proc/1/ns/mnt", F_OK) != 0)
return HIDE_NO_NS; return HIDE_NO_NS;
if (procfp == nullptr && (procfp = opendir("/proc")) == nullptr) if (procfp == nullptr && (procfp = opendir("/proc")) == nullptr)
return DAEMON_ERROR; return DAEMON_ERROR;
LOGI("* Starting MagiskHide\n"); LOGI("* Starting MagiskHide\n");
// Initialize the mutex lock // Initialize the mutex lock
pthread_mutex_init(&monitor_lock, nullptr); pthread_mutex_init(&monitor_lock, nullptr);
// Initialize the hide list // Initialize the hide list
if (!init_list()) if (!init_list())
return DAEMON_ERROR; return DAEMON_ERROR;
hide_sensitive_props(); hide_sensitive_props();
if (DAEMON_STATE >= STATE_BOOT_COMPLETE || DAEMON_STATE == STATE_NONE) if (DAEMON_STATE >= STATE_BOOT_COMPLETE || DAEMON_STATE == STATE_NONE)
hide_late_sensitive_props(); hide_late_sensitive_props();
// Start monitoring // Start monitoring
void *(*start)(void*) = [](void*) -> void* { proc_monitor(); }; void *(*start)(void*) = [](void*) -> void* { proc_monitor(); };
if (xpthread_create(&proc_monitor_thread, nullptr, start, nullptr)) if (xpthread_create(&proc_monitor_thread, nullptr, start, nullptr))
return DAEMON_ERROR; return DAEMON_ERROR;
hide_state = true; hide_state = true;
update_hide_config(); update_hide_config();
return DAEMON_SUCCESS; return DAEMON_SUCCESS;
} }
int stop_magiskhide() { int stop_magiskhide() {
mutex_guard g(hide_state_lock); mutex_guard g(hide_state_lock);
if (hide_state) { if (hide_state) {
LOGI("* Stopping MagiskHide\n"); LOGI("* Stopping MagiskHide\n");
pthread_kill(proc_monitor_thread, SIGTERMTHRD); pthread_kill(proc_monitor_thread, SIGTERMTHRD);
} }
hide_state = false; hide_state = false;
update_hide_config(); update_hide_config();
return DAEMON_SUCCESS; return DAEMON_SUCCESS;
} }
void auto_start_magiskhide() { void auto_start_magiskhide() {
if (hide_enabled()) { if (hide_enabled()) {
pthread_kill(proc_monitor_thread, SIGALRM); pthread_kill(proc_monitor_thread, SIGALRM);
hide_late_sensitive_props(); hide_late_sensitive_props();
} else if (SDK_INT >= 19) { } else if (SDK_INT >= 19) {
db_settings dbs; db_settings dbs;
get_db_settings(dbs, HIDE_CONFIG); get_db_settings(dbs, HIDE_CONFIG);
if (dbs[HIDE_CONFIG]) if (dbs[HIDE_CONFIG])
launch_magiskhide(); launch_magiskhide();
} }
} }
void test_proc_monitor() { void test_proc_monitor() {
if (procfp == nullptr && (procfp = opendir("/proc")) == nullptr) if (procfp == nullptr && (procfp = opendir("/proc")) == nullptr)
exit(1); exit(1);
proc_monitor(); proc_monitor();
} }

View File

@@ -16,144 +16,144 @@
using namespace std::literals; using namespace std::literals;
[[noreturn]] static void usage(char *arg0) { [[noreturn]] static void usage(char *arg0) {
fprintf(stderr, fprintf(stderr,
"MagiskHide - Hide Config CLI\n\n" "MagiskHide - Hide Config CLI\n\n"
"Usage: %s [action [arguments...] ]\n\n" "Usage: %s [action [arguments...] ]\n\n"
"Actions:\n" "Actions:\n"
" status Return the status of magiskhide\n" " status Return the status of magiskhide\n"
" enable Start magiskhide\n" " enable Start magiskhide\n"
" disable Stop magiskhide\n" " disable Stop magiskhide\n"
" add PKG [PROC] Add a new target to the hide list\n" " add PKG [PROC] Add a new target to the hide list\n"
" rm PKG [PROC] Remove target(s) from the hide list\n" " rm PKG [PROC] Remove target(s) from the hide list\n"
" ls Print the current hide list\n" " ls Print the current hide list\n"
" exec CMDs... Execute commands in isolated mount\n" " exec CMDs... Execute commands in isolated mount\n"
" namespace and do all hide unmounts\n" " namespace and do all hide unmounts\n"
#ifdef MAGISK_DEBUG #ifdef MAGISK_DEBUG
" test Run process monitor test\n" " test Run process monitor test\n"
#endif #endif
, arg0); , arg0);
exit(1); exit(1);
} }
void magiskhide_handler(int client) { void magiskhide_handler(int client) {
int req = read_int(client); int req = read_int(client);
int res = DAEMON_ERROR; int res = DAEMON_ERROR;
switch (req) { switch (req) {
case STOP_MAGISKHIDE: case STOP_MAGISKHIDE:
case ADD_HIDELIST: case ADD_HIDELIST:
case RM_HIDELIST: case RM_HIDELIST:
case LS_HIDELIST: case LS_HIDELIST:
if (!hide_enabled()) { if (!hide_enabled()) {
write_int(client, HIDE_NOT_ENABLED); write_int(client, HIDE_NOT_ENABLED);
close(client); close(client);
return; return;
} }
} }
switch (req) { switch (req) {
case LAUNCH_MAGISKHIDE: case LAUNCH_MAGISKHIDE:
res = launch_magiskhide(); res = launch_magiskhide();
break; break;
case STOP_MAGISKHIDE: case STOP_MAGISKHIDE:
res = stop_magiskhide(); res = stop_magiskhide();
break; break;
case ADD_HIDELIST: case ADD_HIDELIST:
res = add_list(client); res = add_list(client);
break; break;
case RM_HIDELIST: case RM_HIDELIST:
res = rm_list(client); res = rm_list(client);
break; break;
case LS_HIDELIST: case LS_HIDELIST:
ls_list(client); ls_list(client);
return; return;
case HIDE_STATUS: case HIDE_STATUS:
res = hide_enabled() ? HIDE_IS_ENABLED : HIDE_NOT_ENABLED; res = hide_enabled() ? HIDE_IS_ENABLED : HIDE_NOT_ENABLED;
break; break;
} }
write_int(client, res); write_int(client, res);
close(client); close(client);
} }
int magiskhide_main(int argc, char *argv[]) { int magiskhide_main(int argc, char *argv[]) {
if (argc < 2) if (argc < 2)
usage(argv[0]); usage(argv[0]);
// CLI backwards compatibility // CLI backwards compatibility
const char *opt = argv[1]; const char *opt = argv[1];
if (opt[0] == '-' && opt[1] == '-') if (opt[0] == '-' && opt[1] == '-')
opt += 2; opt += 2;
int req; int req;
if (opt == "enable"sv) if (opt == "enable"sv)
req = LAUNCH_MAGISKHIDE; req = LAUNCH_MAGISKHIDE;
else if (opt == "disable"sv) else if (opt == "disable"sv)
req = STOP_MAGISKHIDE; req = STOP_MAGISKHIDE;
else if (opt == "add"sv) else if (opt == "add"sv)
req = ADD_HIDELIST; req = ADD_HIDELIST;
else if (opt == "rm"sv) else if (opt == "rm"sv)
req = RM_HIDELIST; req = RM_HIDELIST;
else if (opt == "ls"sv) else if (opt == "ls"sv)
req = LS_HIDELIST; req = LS_HIDELIST;
else if (opt == "status"sv) else if (opt == "status"sv)
req = HIDE_STATUS; req = HIDE_STATUS;
else if (opt == "exec"sv && argc > 2) { else if (opt == "exec"sv && argc > 2) {
xunshare(CLONE_NEWNS); xunshare(CLONE_NEWNS);
xmount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr); xmount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr);
hide_unmount(); hide_unmount();
execvp(argv[2], argv + 2); execvp(argv[2], argv + 2);
exit(1); exit(1);
} }
#if 0 #if 0
else if (opt == "test"sv) else if (opt == "test"sv)
test_proc_monitor(); test_proc_monitor();
#endif #endif
else else
usage(argv[0]); usage(argv[0]);
// Send request // Send request
int fd = connect_daemon(); int fd = connect_daemon();
write_int(fd, MAGISKHIDE); write_int(fd, MAGISKHIDE);
write_int(fd, req); write_int(fd, req);
if (req == ADD_HIDELIST || req == RM_HIDELIST) { if (req == ADD_HIDELIST || req == RM_HIDELIST) {
write_string(fd, argv[2]); write_string(fd, argv[2]);
write_string(fd, argv[3] ? argv[3] : ""); write_string(fd, argv[3] ? argv[3] : "");
} }
if (req == LS_HIDELIST) if (req == LS_HIDELIST)
send_fd(fd, STDOUT_FILENO); send_fd(fd, STDOUT_FILENO);
// Get response // Get response
int code = read_int(fd); int code = read_int(fd);
switch (code) { switch (code) {
case DAEMON_SUCCESS: case DAEMON_SUCCESS:
break; break;
case HIDE_NOT_ENABLED: case HIDE_NOT_ENABLED:
fprintf(stderr, "MagiskHide is not enabled\n"); fprintf(stderr, "MagiskHide is not enabled\n");
break; break;
case HIDE_IS_ENABLED: case HIDE_IS_ENABLED:
fprintf(stderr, "MagiskHide is enabled\n"); fprintf(stderr, "MagiskHide is enabled\n");
break; break;
case HIDE_ITEM_EXIST: case HIDE_ITEM_EXIST:
fprintf(stderr, "Target already exists in hide list\n"); fprintf(stderr, "Target already exists in hide list\n");
break; break;
case HIDE_ITEM_NOT_EXIST: case HIDE_ITEM_NOT_EXIST:
fprintf(stderr, "Target does not exist in hide list\n"); fprintf(stderr, "Target does not exist in hide list\n");
break; break;
case HIDE_NO_NS: case HIDE_NO_NS:
fprintf(stderr, "Your kernel doesn't support mount namespace\n"); fprintf(stderr, "Your kernel doesn't support mount namespace\n");
break; break;
case HIDE_INVALID_PKG: case HIDE_INVALID_PKG:
fprintf(stderr, "Invalid package / process name\n"); fprintf(stderr, "Invalid package / process name\n");
break; break;
case ROOT_REQUIRED: case ROOT_REQUIRED:
fprintf(stderr, "Root is required for this operation\n"); fprintf(stderr, "Root is required for this operation\n");
break; break;
case DAEMON_ERROR: case DAEMON_ERROR:
default: default:
fprintf(stderr, "Daemon error\n"); fprintf(stderr, "Daemon error\n");
return DAEMON_ERROR; return DAEMON_ERROR;
} }
return req == HIDE_STATUS ? (code == HIDE_IS_ENABLED ? 0 : 1) : code != DAEMON_SUCCESS; return req == HIDE_STATUS ? (code == HIDE_IS_ENABLED ? 0 : 1) : code != DAEMON_SUCCESS;
} }

View File

@@ -43,19 +43,19 @@ extern pthread_mutex_t monitor_lock;
extern std::set<std::pair<std::string, std::string>> hide_set; extern std::set<std::pair<std::string, std::string>> hide_set;
enum { enum {
LAUNCH_MAGISKHIDE, LAUNCH_MAGISKHIDE,
STOP_MAGISKHIDE, STOP_MAGISKHIDE,
ADD_HIDELIST, ADD_HIDELIST,
RM_HIDELIST, RM_HIDELIST,
LS_HIDELIST, LS_HIDELIST,
HIDE_STATUS, HIDE_STATUS,
}; };
enum { enum {
HIDE_IS_ENABLED = DAEMON_LAST, HIDE_IS_ENABLED = DAEMON_LAST,
HIDE_NOT_ENABLED, HIDE_NOT_ENABLED,
HIDE_ITEM_EXIST, HIDE_ITEM_EXIST,
HIDE_ITEM_NOT_EXIST, HIDE_ITEM_NOT_EXIST,
HIDE_NO_NS, HIDE_NO_NS,
HIDE_INVALID_PKG HIDE_INVALID_PKG
}; };

View File

@@ -33,11 +33,11 @@ pthread_mutex_t monitor_lock;
#define PID_MAX 32768 #define PID_MAX 32768
struct pid_set { struct pid_set {
bitset<PID_MAX>::const_reference operator[](size_t pos) const { return set[pos - 1]; } bitset<PID_MAX>::const_reference operator[](size_t pos) const { return set[pos - 1]; }
bitset<PID_MAX>::reference operator[](size_t pos) { return set[pos - 1]; } bitset<PID_MAX>::reference operator[](size_t pos) { return set[pos - 1]; }
void reset() { set.reset(); } void reset() { set.reset(); }
private: private:
bitset<PID_MAX> set; bitset<PID_MAX> set;
}; };
// true if pid is monitored // true if pid is monitored
@@ -48,122 +48,122 @@ pid_set attaches;
********/ ********/
static inline int read_ns(const int pid, struct stat *st) { static inline int read_ns(const int pid, struct stat *st) {
char path[32]; char path[32];
sprintf(path, "/proc/%d/ns/mnt", pid); sprintf(path, "/proc/%d/ns/mnt", pid);
return stat(path, st); return stat(path, st);
} }
static int parse_ppid(int pid) { static int parse_ppid(int pid) {
char path[32]; char path[32];
int ppid; int ppid;
sprintf(path, "/proc/%d/stat", pid); sprintf(path, "/proc/%d/stat", pid);
auto stat = open_file(path, "re"); auto stat = open_file(path, "re");
if (!stat) if (!stat)
return -1; return -1;
// PID COMM STATE PPID ..... // PID COMM STATE PPID .....
fscanf(stat.get(), "%*d %*s %*c %d", &ppid); fscanf(stat.get(), "%*d %*s %*c %d", &ppid);
return ppid; return ppid;
} }
static inline long xptrace(int request, pid_t pid, void *addr, void *data) { static inline long xptrace(int request, pid_t pid, void *addr, void *data) {
long ret = ptrace(request, pid, addr, data); long ret = ptrace(request, pid, addr, data);
if (ret < 0) if (ret < 0)
PLOGE("ptrace %d", pid); PLOGE("ptrace %d", pid);
return ret; return ret;
} }
static inline long xptrace(int request, pid_t pid, void *addr = nullptr, intptr_t data = 0) { static inline long xptrace(int request, pid_t pid, void *addr = nullptr, intptr_t data = 0) {
return xptrace(request, pid, addr, reinterpret_cast<void *>(data)); return xptrace(request, pid, addr, reinterpret_cast<void *>(data));
} }
void update_uid_map() { void update_uid_map() {
mutex_guard lock(monitor_lock); mutex_guard lock(monitor_lock);
uid_proc_map.clear(); uid_proc_map.clear();
string data_path(APP_DATA_DIR); string data_path(APP_DATA_DIR);
size_t len = data_path.length(); size_t len = data_path.length();
auto dir = open_dir(APP_DATA_DIR); auto dir = open_dir(APP_DATA_DIR);
bool first_iter = true; bool first_iter = true;
for (dirent *entry; (entry = xreaddir(dir.get()));) { for (dirent *entry; (entry = xreaddir(dir.get()));) {
data_path.resize(len); data_path.resize(len);
data_path += '/'; data_path += '/';
data_path += entry->d_name; // multiuser user id data_path += entry->d_name; // multiuser user id
data_path += '/'; data_path += '/';
size_t user_len = data_path.length(); size_t user_len = data_path.length();
struct stat st; struct stat st;
for (auto &hide : hide_set) { for (auto &hide : hide_set) {
if (hide.first == ISOLATED_MAGIC) { if (hide.first == ISOLATED_MAGIC) {
if (!first_iter) continue; if (!first_iter) continue;
// Setup isolated processes // Setup isolated processes
uid_proc_map[-1].emplace_back(hide.second); uid_proc_map[-1].emplace_back(hide.second);
} }
data_path.resize(user_len); data_path.resize(user_len);
data_path += hide.first; data_path += hide.first;
if (stat(data_path.data(), &st)) if (stat(data_path.data(), &st))
continue; continue;
uid_proc_map[st.st_uid].emplace_back(hide.second); uid_proc_map[st.st_uid].emplace_back(hide.second);
} }
first_iter = false; first_iter = false;
} }
} }
static bool is_zygote_done() { static bool is_zygote_done() {
#ifdef __LP64__ #ifdef __LP64__
return zygote_map.size() >= 2; return zygote_map.size() >= 2;
#else #else
return zygote_map.size() >= 1; return zygote_map.size() >= 1;
#endif #endif
} }
static void check_zygote() { static void check_zygote() {
crawl_procfs([](int pid) -> bool { crawl_procfs([](int pid) -> bool {
char buf[512]; char buf[512];
snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid); snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
if (FILE *f = fopen(buf, "re")) { if (FILE *f = fopen(buf, "re")) {
fgets(buf, sizeof(buf), f); fgets(buf, sizeof(buf), f);
if (strncmp(buf, "zygote", 6) == 0 && parse_ppid(pid) == 1) if (strncmp(buf, "zygote", 6) == 0 && parse_ppid(pid) == 1)
new_zygote(pid); new_zygote(pid);
fclose(f); fclose(f);
} }
return true; return true;
}); });
if (is_zygote_done()) { if (is_zygote_done()) {
// Stop periodic scanning // Stop periodic scanning
timeval val { .tv_sec = 0, .tv_usec = 0 }; timeval val { .tv_sec = 0, .tv_usec = 0 };
itimerval interval { .it_interval = val, .it_value = val }; itimerval interval { .it_interval = val, .it_value = val };
setitimer(ITIMER_REAL, &interval, nullptr); setitimer(ITIMER_REAL, &interval, nullptr);
} }
} }
#define APP_PROC "/system/bin/app_process" #define APP_PROC "/system/bin/app_process"
static void setup_inotify() { static void setup_inotify() {
inotify_fd = xinotify_init1(IN_CLOEXEC); inotify_fd = xinotify_init1(IN_CLOEXEC);
if (inotify_fd < 0) if (inotify_fd < 0)
term_thread(); term_thread();
// Setup inotify asynchronous I/O // Setup inotify asynchronous I/O
fcntl(inotify_fd, F_SETFL, O_ASYNC); fcntl(inotify_fd, F_SETFL, O_ASYNC);
struct f_owner_ex ex = { struct f_owner_ex ex = {
.type = F_OWNER_TID, .type = F_OWNER_TID,
.pid = gettid() .pid = gettid()
}; };
fcntl(inotify_fd, F_SETOWN_EX, &ex); fcntl(inotify_fd, F_SETOWN_EX, &ex);
// Monitor packages.xml // Monitor packages.xml
inotify_add_watch(inotify_fd, "/data/system", IN_CLOSE_WRITE); inotify_add_watch(inotify_fd, "/data/system", IN_CLOSE_WRITE);
// Monitor app_process // Monitor app_process
if (access(APP_PROC "32", F_OK) == 0) { if (access(APP_PROC "32", F_OK) == 0) {
inotify_add_watch(inotify_fd, APP_PROC "32", IN_ACCESS); inotify_add_watch(inotify_fd, APP_PROC "32", IN_ACCESS);
if (access(APP_PROC "64", F_OK) == 0) if (access(APP_PROC "64", F_OK) == 0)
inotify_add_watch(inotify_fd, APP_PROC "64", IN_ACCESS); inotify_add_watch(inotify_fd, APP_PROC "64", IN_ACCESS);
} else { } else {
inotify_add_watch(inotify_fd, APP_PROC, IN_ACCESS); inotify_add_watch(inotify_fd, APP_PROC, IN_ACCESS);
} }
} }
/************************ /************************
@@ -171,37 +171,37 @@ static void setup_inotify() {
************************/ ************************/
static void inotify_event(int) { static void inotify_event(int) {
/* Make sure we can actually read stuffs /* Make sure we can actually read stuffs
* or else the whole thread will be blocked.*/ * or else the whole thread will be blocked.*/
struct pollfd pfd = { struct pollfd pfd = {
.fd = inotify_fd, .fd = inotify_fd,
.events = POLLIN, .events = POLLIN,
.revents = 0 .revents = 0
}; };
if (poll(&pfd, 1, 0) <= 0) if (poll(&pfd, 1, 0) <= 0)
return; // Nothing to read return; // Nothing to read
char buf[512]; char buf[512];
auto event = reinterpret_cast<struct inotify_event *>(buf); auto event = reinterpret_cast<struct inotify_event *>(buf);
read(inotify_fd, buf, sizeof(buf)); read(inotify_fd, buf, sizeof(buf));
if ((event->mask & IN_CLOSE_WRITE) && event->name == "packages.xml"sv) if ((event->mask & IN_CLOSE_WRITE) && event->name == "packages.xml"sv)
update_uid_map(); update_uid_map();
check_zygote(); check_zygote();
} }
// Workaround for the lack of pthread_cancel // Workaround for the lack of pthread_cancel
static void term_thread(int) { static void term_thread(int) {
LOGD("proc_monitor: cleaning up\n"); LOGD("proc_monitor: cleaning up\n");
uid_proc_map.clear(); uid_proc_map.clear();
zygote_map.clear(); zygote_map.clear();
hide_set.clear(); hide_set.clear();
attaches.reset(); attaches.reset();
// Misc // Misc
set_hide_state(false); set_hide_state(false);
pthread_mutex_destroy(&monitor_lock); pthread_mutex_destroy(&monitor_lock);
close(inotify_fd); close(inotify_fd);
inotify_fd = -1; inotify_fd = -1;
LOGD("proc_monitor: terminate\n"); LOGD("proc_monitor: terminate\n");
pthread_exit(nullptr); pthread_exit(nullptr);
} }
/****************** /******************
@@ -216,246 +216,246 @@ static void term_thread(int) {
#define PTRACE_LOG(...) #define PTRACE_LOG(...)
static void detach_pid(int pid, int signal = 0) { static void detach_pid(int pid, int signal = 0) {
attaches[pid] = false; attaches[pid] = false;
ptrace(PTRACE_DETACH, pid, 0, signal); ptrace(PTRACE_DETACH, pid, 0, signal);
PTRACE_LOG("detach\n"); PTRACE_LOG("detach\n");
} }
static bool check_pid(int pid) { static bool check_pid(int pid) {
char path[128]; char path[128];
char cmdline[1024]; char cmdline[1024];
struct stat st; struct stat st;
sprintf(path, "/proc/%d", pid); sprintf(path, "/proc/%d", pid);
if (stat(path, &st)) { if (stat(path, &st)) {
// Process died unexpectedly, ignore // Process died unexpectedly, ignore
detach_pid(pid); detach_pid(pid);
return true; return true;
} }
// UID hasn't changed // UID hasn't changed
if (st.st_uid == 0) if (st.st_uid == 0)
return false; return false;
sprintf(path, "/proc/%d/cmdline", pid); sprintf(path, "/proc/%d/cmdline", pid);
if (auto f = open_file(path, "re")) { if (auto f = open_file(path, "re")) {
fgets(cmdline, sizeof(cmdline), f.get()); fgets(cmdline, sizeof(cmdline), f.get());
} else { } else {
// Process died unexpectedly, ignore // Process died unexpectedly, ignore
detach_pid(pid); detach_pid(pid);
return true; return true;
} }
if (cmdline == "zygote"sv || cmdline == "zygote32"sv || cmdline == "zygote64"sv || if (cmdline == "zygote"sv || cmdline == "zygote32"sv || cmdline == "zygote64"sv ||
cmdline == "usap32"sv || cmdline == "usap64"sv) cmdline == "usap32"sv || cmdline == "usap64"sv)
return false; return false;
int uid = st.st_uid; int uid = st.st_uid;
auto it = uid_proc_map.end(); auto it = uid_proc_map.end();
if (uid % 100000 > 90000) { if (uid % 100000 > 90000) {
// Isolated process // Isolated process
it = uid_proc_map.find(-1); it = uid_proc_map.find(-1);
if (it == uid_proc_map.end()) if (it == uid_proc_map.end())
goto not_target; goto not_target;
for (auto &s : it->second) { for (auto &s : it->second) {
if (str_starts(cmdline, s)) { if (str_starts(cmdline, s)) {
LOGI("proc_monitor: (isolated) [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid); LOGI("proc_monitor: (isolated) [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid);
goto inject_and_hide; goto inject_and_hide;
} }
} }
} }
it = uid_proc_map.find(uid); it = uid_proc_map.find(uid);
if (it == uid_proc_map.end()) if (it == uid_proc_map.end())
goto not_target; goto not_target;
for (auto &s : it->second) { for (auto &s : it->second) {
if (s != cmdline) if (s != cmdline)
continue; continue;
if (str_ends(s, "_zygote")) { if (str_ends(s, "_zygote")) {
LOGI("proc_monitor: (app zygote) [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid); LOGI("proc_monitor: (app zygote) [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid);
goto inject_and_hide; goto inject_and_hide;
} }
// Double check whether ns is separated // Double check whether ns is separated
read_ns(pid, &st); read_ns(pid, &st);
for (auto &zit : zygote_map) { for (auto &zit : zygote_map) {
if (zit.second.st_ino == st.st_ino && if (zit.second.st_ino == st.st_ino &&
zit.second.st_dev == st.st_dev) { zit.second.st_dev == st.st_dev) {
// For some reason ns is not separated, abort // For some reason ns is not separated, abort
goto not_target; goto not_target;
} }
} }
// Finally this is our target! // Finally this is our target!
// Detach from ptrace but should still remain stopped. // Detach from ptrace but should still remain stopped.
// The hide daemon will resume the process. // The hide daemon will resume the process.
LOGI("proc_monitor: [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid); LOGI("proc_monitor: [%s] PID=[%d] UID=[%d]\n", cmdline, pid, uid);
detach_pid(pid, SIGSTOP); detach_pid(pid, SIGSTOP);
hide_daemon(pid); hide_daemon(pid);
return true; return true;
} }
not_target: not_target:
PTRACE_LOG("[%s] is not our target\n", cmdline); PTRACE_LOG("[%s] is not our target\n", cmdline);
detach_pid(pid); detach_pid(pid);
return true; return true;
inject_and_hide: inject_and_hide:
// TODO: handle isolated processes and app zygotes // TODO: handle isolated processes and app zygotes
detach_pid(pid); detach_pid(pid);
return true; return true;
} }
static bool is_process(int pid) { static bool is_process(int pid) {
char buf[128]; char buf[128];
char key[32]; char key[32];
int tgid; int tgid;
sprintf(buf, "/proc/%d/status", pid); sprintf(buf, "/proc/%d/status", pid);
auto fp = open_file(buf, "re"); auto fp = open_file(buf, "re");
// PID is dead // PID is dead
if (!fp) if (!fp)
return false; return false;
while (fgets(buf, sizeof(buf), fp.get())) { while (fgets(buf, sizeof(buf), fp.get())) {
sscanf(buf, "%s", key); sscanf(buf, "%s", key);
if (key == "Tgid:"sv) { if (key == "Tgid:"sv) {
sscanf(buf, "%*s %d", &tgid); sscanf(buf, "%*s %d", &tgid);
return tgid == pid; return tgid == pid;
} }
} }
return false; return false;
} }
static void new_zygote(int pid) { static void new_zygote(int pid) {
struct stat st; struct stat st;
if (read_ns(pid, &st)) if (read_ns(pid, &st))
return; return;
auto it = zygote_map.find(pid); auto it = zygote_map.find(pid);
if (it != zygote_map.end()) { if (it != zygote_map.end()) {
// Update namespace info // Update namespace info
it->second = st; it->second = st;
return; return;
} }
LOGD("proc_monitor: ptrace zygote PID=[%d]\n", pid); LOGD("proc_monitor: ptrace zygote PID=[%d]\n", pid);
zygote_map[pid] = st; zygote_map[pid] = st;
xptrace(PTRACE_ATTACH, pid); xptrace(PTRACE_ATTACH, pid);
waitpid(pid, nullptr, __WALL | __WNOTHREAD); waitpid(pid, nullptr, __WALL | __WNOTHREAD);
xptrace(PTRACE_SETOPTIONS, pid, nullptr, xptrace(PTRACE_SETOPTIONS, pid, nullptr,
PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXIT); PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXIT);
xptrace(PTRACE_CONT, pid); xptrace(PTRACE_CONT, pid);
} }
#define WEVENT(s) (((s) & 0xffff0000) >> 16) #define WEVENT(s) (((s) & 0xffff0000) >> 16)
#define DETACH_AND_CONT { detach_pid(pid); continue; } #define DETACH_AND_CONT { detach_pid(pid); continue; }
void proc_monitor() { void proc_monitor() {
// Unblock some signals // Unblock some signals
sigset_t block_set; sigset_t block_set;
sigemptyset(&block_set); sigemptyset(&block_set);
sigaddset(&block_set, SIGTERMTHRD); sigaddset(&block_set, SIGTERMTHRD);
sigaddset(&block_set, SIGIO); sigaddset(&block_set, SIGIO);
sigaddset(&block_set, SIGALRM); sigaddset(&block_set, SIGALRM);
pthread_sigmask(SIG_UNBLOCK, &block_set, nullptr); pthread_sigmask(SIG_UNBLOCK, &block_set, nullptr);
struct sigaction act{}; struct sigaction act{};
act.sa_handler = term_thread; act.sa_handler = term_thread;
sigaction(SIGTERMTHRD, &act, nullptr); sigaction(SIGTERMTHRD, &act, nullptr);
act.sa_handler = inotify_event; act.sa_handler = inotify_event;
sigaction(SIGIO, &act, nullptr); sigaction(SIGIO, &act, nullptr);
act.sa_handler = [](int){ check_zygote(); }; act.sa_handler = [](int){ check_zygote(); };
sigaction(SIGALRM, &act, nullptr); sigaction(SIGALRM, &act, nullptr);
setup_inotify(); setup_inotify();
// First try find existing zygotes // First try find existing zygotes
check_zygote(); check_zygote();
if (!is_zygote_done()) { if (!is_zygote_done()) {
// Periodic scan every 250ms // Periodic scan every 250ms
timeval val { .tv_sec = 0, .tv_usec = 250000 }; timeval val { .tv_sec = 0, .tv_usec = 250000 };
itimerval interval { .it_interval = val, .it_value = val }; itimerval interval { .it_interval = val, .it_value = val };
setitimer(ITIMER_REAL, &interval, nullptr); setitimer(ITIMER_REAL, &interval, nullptr);
} }
for (int status;;) { for (int status;;) {
const int pid = waitpid(-1, &status, __WALL | __WNOTHREAD); const int pid = waitpid(-1, &status, __WALL | __WNOTHREAD);
if (pid < 0) { if (pid < 0) {
if (errno == ECHILD) { if (errno == ECHILD) {
// Nothing to wait yet, sleep and wait till signal interruption // Nothing to wait yet, sleep and wait till signal interruption
LOGD("proc_monitor: nothing to monitor, wait for signal\n"); LOGD("proc_monitor: nothing to monitor, wait for signal\n");
struct timespec ts = { struct timespec ts = {
.tv_sec = INT_MAX, .tv_sec = INT_MAX,
.tv_nsec = 0 .tv_nsec = 0
}; };
nanosleep(&ts, nullptr); nanosleep(&ts, nullptr);
} }
continue; continue;
} }
if (!WIFSTOPPED(status) /* Ignore if not ptrace-stop */) if (!WIFSTOPPED(status) /* Ignore if not ptrace-stop */)
DETACH_AND_CONT; DETACH_AND_CONT;
int event = WEVENT(status); int event = WEVENT(status);
int signal = WSTOPSIG(status); int signal = WSTOPSIG(status);
if (signal == SIGTRAP && event) { if (signal == SIGTRAP && event) {
unsigned long msg; unsigned long msg;
xptrace(PTRACE_GETEVENTMSG, pid, nullptr, &msg); xptrace(PTRACE_GETEVENTMSG, pid, nullptr, &msg);
if (zygote_map.count(pid)) { if (zygote_map.count(pid)) {
// Zygote event // Zygote event
switch (event) { switch (event) {
case PTRACE_EVENT_FORK: case PTRACE_EVENT_FORK:
case PTRACE_EVENT_VFORK: case PTRACE_EVENT_VFORK:
PTRACE_LOG("zygote forked: [%lu]\n", msg); PTRACE_LOG("zygote forked: [%lu]\n", msg);
attaches[msg] = true; attaches[msg] = true;
break; break;
case PTRACE_EVENT_EXIT: case PTRACE_EVENT_EXIT:
PTRACE_LOG("zygote exited with status: [%lu]\n", msg); PTRACE_LOG("zygote exited with status: [%lu]\n", msg);
[[fallthrough]]; [[fallthrough]];
default: default:
zygote_map.erase(pid); zygote_map.erase(pid);
DETACH_AND_CONT; DETACH_AND_CONT;
} }
} else { } else {
switch (event) { switch (event) {
case PTRACE_EVENT_CLONE: case PTRACE_EVENT_CLONE:
PTRACE_LOG("create new threads: [%lu]\n", msg); PTRACE_LOG("create new threads: [%lu]\n", msg);
if (attaches[pid] && check_pid(pid)) if (attaches[pid] && check_pid(pid))
continue; continue;
break; break;
case PTRACE_EVENT_EXEC: case PTRACE_EVENT_EXEC:
case PTRACE_EVENT_EXIT: case PTRACE_EVENT_EXIT:
PTRACE_LOG("exit or execve\n"); PTRACE_LOG("exit or execve\n");
[[fallthrough]]; [[fallthrough]];
default: default:
DETACH_AND_CONT; DETACH_AND_CONT;
} }
} }
xptrace(PTRACE_CONT, pid); xptrace(PTRACE_CONT, pid);
} else if (signal == SIGSTOP) { } else if (signal == SIGSTOP) {
if (!attaches[pid]) { if (!attaches[pid]) {
// Double check if this is actually a process // Double check if this is actually a process
attaches[pid] = is_process(pid); attaches[pid] = is_process(pid);
} }
if (attaches[pid]) { if (attaches[pid]) {
// This is a process, continue monitoring // This is a process, continue monitoring
PTRACE_LOG("SIGSTOP from child\n"); PTRACE_LOG("SIGSTOP from child\n");
xptrace(PTRACE_SETOPTIONS, pid, nullptr, xptrace(PTRACE_SETOPTIONS, pid, nullptr,
PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT); PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT);
xptrace(PTRACE_CONT, pid); xptrace(PTRACE_CONT, pid);
} else { } else {
// This is a thread, do NOT monitor // This is a thread, do NOT monitor
PTRACE_LOG("SIGSTOP from thread\n"); PTRACE_LOG("SIGSTOP from thread\n");
DETACH_AND_CONT; DETACH_AND_CONT;
} }
} else { } else {
// Not caused by us, resend signal // Not caused by us, resend signal
xptrace(PTRACE_CONT, pid, nullptr, signal); xptrace(PTRACE_CONT, pid, nullptr, signal);
PTRACE_LOG("signal [%d]\n", signal); PTRACE_LOG("signal [%d]\n", signal);
} }
} }
} }

View File

@@ -6,7 +6,7 @@
using namespace std::literals; using namespace std::literals;
[[noreturn]] static void usage(char *arg0) { [[noreturn]] static void usage(char *arg0) {
fprintf(stderr, fprintf(stderr,
R"EOF(MagiskPolicy - Sepolicy Patch Tool R"EOF(MagiskPolicy - Sepolicy Patch Tool
Usage: %s [--options...] [policy statements...] Usage: %s [--options...] [policy statements...]
@@ -28,93 +28,93 @@ If neither --load or --compile-split is specified, it will load
from current live policies (/sys/fs/selinux/policy) from current live policies (/sys/fs/selinux/policy)
)EOF", arg0); )EOF", arg0);
exit(1); exit(1);
} }
int magiskpolicy_main(int argc, char *argv[]) { int magiskpolicy_main(int argc, char *argv[]) {
cmdline_logging(); cmdline_logging();
const char *out_file = nullptr; const char *out_file = nullptr;
const char *rule_file = nullptr; const char *rule_file = nullptr;
sepolicy *sepol = nullptr; sepolicy *sepol = nullptr;
bool magisk = false; bool magisk = false;
bool live = false; bool live = false;
if (argc < 2) usage(argv[0]); if (argc < 2) usage(argv[0]);
int i = 1; int i = 1;
for (; i < argc; ++i) { for (; i < argc; ++i) {
// Parse options // Parse options
if (argv[i][0] == '-' && argv[i][1] == '-') { if (argv[i][0] == '-' && argv[i][1] == '-') {
auto option = argv[i] + 2; auto option = argv[i] + 2;
if (option == "live"sv) if (option == "live"sv)
live = true; live = true;
else if (option == "magisk"sv) else if (option == "magisk"sv)
magisk = true; magisk = true;
else if (option == "load"sv) { else if (option == "load"sv) {
if (argv[i + 1] == nullptr) if (argv[i + 1] == nullptr)
usage(argv[0]); usage(argv[0]);
sepol = sepolicy::from_file(argv[i + 1]); sepol = sepolicy::from_file(argv[i + 1]);
if (!sepol) { if (!sepol) {
fprintf(stderr, "Cannot load policy from %s\n", argv[i + 1]); fprintf(stderr, "Cannot load policy from %s\n", argv[i + 1]);
return 1; return 1;
} }
++i; ++i;
} else if (option == "load-split"sv) { } else if (option == "load-split"sv) {
sepol = sepolicy::from_split(); sepol = sepolicy::from_split();
if (!sepol) { if (!sepol) {
fprintf(stderr, "Cannot load split cil\n"); fprintf(stderr, "Cannot load split cil\n");
return 1; return 1;
} }
} else if (option == "compile-split"sv) { } else if (option == "compile-split"sv) {
sepol = sepolicy::compile_split(); sepol = sepolicy::compile_split();
if (!sepol) { if (!sepol) {
fprintf(stderr, "Cannot compile split cil\n"); fprintf(stderr, "Cannot compile split cil\n");
return 1; return 1;
} }
} else if (option == "save"sv) { } else if (option == "save"sv) {
if (argv[i + 1] == nullptr) if (argv[i + 1] == nullptr)
usage(argv[0]); usage(argv[0]);
out_file = argv[i + 1]; out_file = argv[i + 1];
++i; ++i;
} else if (option == "apply"sv) { } else if (option == "apply"sv) {
if (argv[i + 1] == nullptr) if (argv[i + 1] == nullptr)
usage(argv[0]); usage(argv[0]);
rule_file = argv[i + 1]; rule_file = argv[i + 1];
++i; ++i;
} else if (option == "help"sv) { } else if (option == "help"sv) {
statement_help(); statement_help();
} else { } else {
usage(argv[0]); usage(argv[0]);
} }
} else { } else {
break; break;
} }
} }
// Use current policy if nothing is loaded // Use current policy if nothing is loaded
if (sepol == nullptr && !(sepol = sepolicy::from_file(SELINUX_POLICY))) { if (sepol == nullptr && !(sepol = sepolicy::from_file(SELINUX_POLICY))) {
fprintf(stderr, "Cannot load policy from " SELINUX_POLICY "\n"); fprintf(stderr, "Cannot load policy from " SELINUX_POLICY "\n");
return 1; return 1;
} }
if (magisk) if (magisk)
sepol->magisk_rules(); sepol->magisk_rules();
if (rule_file) if (rule_file)
sepol->load_rule_file(rule_file); sepol->load_rule_file(rule_file);
for (; i < argc; ++i) for (; i < argc; ++i)
sepol->parse_statement(argv[i]); sepol->parse_statement(argv[i]);
if (live && !sepol->to_file(SELINUX_LOAD)) { if (live && !sepol->to_file(SELINUX_LOAD)) {
fprintf(stderr, "Cannot apply policy\n"); fprintf(stderr, "Cannot apply policy\n");
return 1; return 1;
} }
if (out_file && !sepol->to_file(out_file)) { if (out_file && !sepol->to_file(out_file)) {
fprintf(stderr, "Cannot dump policy to %s\n", out_file); fprintf(stderr, "Cannot dump policy to %s\n", out_file);
return 1; return 1;
} }
delete sepol; delete sepol;
return 0; return 0;
} }

View File

@@ -13,218 +13,218 @@
#define SHALEN 64 #define SHALEN 64
static bool cmp_sha256(const char *a, const char *b) { static bool cmp_sha256(const char *a, const char *b) {
char id_a[SHALEN] = {0}; char id_a[SHALEN] = {0};
char id_b[SHALEN] = {0}; char id_b[SHALEN] = {0};
if (int fd = xopen(a, O_RDONLY | O_CLOEXEC); fd >= 0) { if (int fd = xopen(a, O_RDONLY | O_CLOEXEC); fd >= 0) {
xread(fd, id_a, SHALEN); xread(fd, id_a, SHALEN);
close(fd); close(fd);
} else { } else {
return false; return false;
} }
if (int fd = xopen(b, O_RDONLY | O_CLOEXEC); fd >= 0) { if (int fd = xopen(b, O_RDONLY | O_CLOEXEC); fd >= 0) {
xread(fd, id_b, SHALEN); xread(fd, id_b, SHALEN);
close(fd); close(fd);
} else { } else {
return false; return false;
} }
LOGD("%s=[%.*s]\n", a, SHALEN, id_a); LOGD("%s=[%.*s]\n", a, SHALEN, id_a);
LOGD("%s=[%.*s]\n", b, SHALEN, id_b); LOGD("%s=[%.*s]\n", b, SHALEN, id_b);
return memcmp(id_a, id_b, SHALEN) == 0; return memcmp(id_a, id_b, SHALEN) == 0;
} }
static bool check_precompiled(const char *precompiled) { static bool check_precompiled(const char *precompiled) {
bool ok = false; bool ok = false;
const char *actual_sha; const char *actual_sha;
char compiled_sha[128]; char compiled_sha[128];
actual_sha = PLAT_POLICY_DIR "plat_and_mapping_sepolicy.cil.sha256"; actual_sha = PLAT_POLICY_DIR "plat_and_mapping_sepolicy.cil.sha256";
if (access(actual_sha, R_OK) == 0) { if (access(actual_sha, R_OK) == 0) {
ok = true; ok = true;
sprintf(compiled_sha, "%s.plat_and_mapping.sha256", precompiled); sprintf(compiled_sha, "%s.plat_and_mapping.sha256", precompiled);
if (!cmp_sha256(actual_sha, compiled_sha)) if (!cmp_sha256(actual_sha, compiled_sha))
return false; return false;
} }
actual_sha = PLAT_POLICY_DIR "plat_sepolicy_and_mapping.sha256"; actual_sha = PLAT_POLICY_DIR "plat_sepolicy_and_mapping.sha256";
if (access(actual_sha, R_OK) == 0) { if (access(actual_sha, R_OK) == 0) {
ok = true; ok = true;
sprintf(compiled_sha, "%s.plat_sepolicy_and_mapping.sha256", precompiled); sprintf(compiled_sha, "%s.plat_sepolicy_and_mapping.sha256", precompiled);
if (!cmp_sha256(actual_sha, compiled_sha)) if (!cmp_sha256(actual_sha, compiled_sha))
return false; return false;
} }
actual_sha = PROD_POLICY_DIR "product_sepolicy_and_mapping.sha256"; actual_sha = PROD_POLICY_DIR "product_sepolicy_and_mapping.sha256";
if (access(actual_sha, R_OK) == 0) { if (access(actual_sha, R_OK) == 0) {
ok = true; ok = true;
sprintf(compiled_sha, "%s.product_sepolicy_and_mapping.sha256", precompiled); sprintf(compiled_sha, "%s.product_sepolicy_and_mapping.sha256", precompiled);
if (!cmp_sha256(actual_sha, compiled_sha) != 0) if (!cmp_sha256(actual_sha, compiled_sha) != 0)
return false; return false;
} }
actual_sha = SYSEXT_POLICY_DIR "system_ext_sepolicy_and_mapping.sha256"; actual_sha = SYSEXT_POLICY_DIR "system_ext_sepolicy_and_mapping.sha256";
if (access(actual_sha, R_OK) == 0) { if (access(actual_sha, R_OK) == 0) {
ok = true; ok = true;
sprintf(compiled_sha, "%s.system_ext_sepolicy_and_mapping.sha256", precompiled); sprintf(compiled_sha, "%s.system_ext_sepolicy_and_mapping.sha256", precompiled);
if (!cmp_sha256(actual_sha, compiled_sha) != 0) if (!cmp_sha256(actual_sha, compiled_sha) != 0)
return false; return false;
} }
return ok; return ok;
} }
static void load_cil(struct cil_db *db, const char *file) { static void load_cil(struct cil_db *db, const char *file) {
char *addr; char *addr;
size_t size; size_t size;
mmap_ro(file, addr, size); mmap_ro(file, addr, size);
cil_add_file(db, (char *) file, addr, size); cil_add_file(db, (char *) file, addr, size);
LOGD("cil_add [%s]\n", file); LOGD("cil_add [%s]\n", file);
munmap(addr, size); munmap(addr, size);
} }
sepolicy *sepolicy::from_file(const char *file) { sepolicy *sepolicy::from_file(const char *file) {
LOGD("Load policy from: %s\n", file); LOGD("Load policy from: %s\n", file);
policy_file_t pf; policy_file_t pf;
policy_file_init(&pf); policy_file_init(&pf);
auto fp = xopen_file(file, "re"); auto fp = xopen_file(file, "re");
pf.fp = fp.get(); pf.fp = fp.get();
pf.type = PF_USE_STDIO; pf.type = PF_USE_STDIO;
auto db = static_cast<policydb_t *>(xmalloc(sizeof(policydb_t))); auto db = static_cast<policydb_t *>(xmalloc(sizeof(policydb_t)));
if (policydb_init(db) || policydb_read(db, &pf, 0)) { if (policydb_init(db) || policydb_read(db, &pf, 0)) {
LOGE("Fail to load policy from %s\n", file); LOGE("Fail to load policy from %s\n", file);
free(db); free(db);
return nullptr; return nullptr;
} }
auto sepol = new sepolicy(); auto sepol = new sepolicy();
sepol->db = db; sepol->db = db;
return sepol; return sepol;
} }
sepolicy *sepolicy::compile_split() { sepolicy *sepolicy::compile_split() {
char path[128], plat_ver[10]; char path[128], plat_ver[10];
cil_db_t *db = nullptr; cil_db_t *db = nullptr;
sepol_policydb_t *pdb = nullptr; sepol_policydb_t *pdb = nullptr;
FILE *f; FILE *f;
int policy_ver; int policy_ver;
const char *cil_file; const char *cil_file;
cil_db_init(&db); cil_db_init(&db);
run_finally fin([db_ptr = &db]{ cil_db_destroy(db_ptr); }); run_finally fin([db_ptr = &db]{ cil_db_destroy(db_ptr); });
cil_set_mls(db, 1); cil_set_mls(db, 1);
cil_set_multiple_decls(db, 1); cil_set_multiple_decls(db, 1);
cil_set_disable_neverallow(db, 1); cil_set_disable_neverallow(db, 1);
cil_set_target_platform(db, SEPOL_TARGET_SELINUX); cil_set_target_platform(db, SEPOL_TARGET_SELINUX);
cil_set_attrs_expand_generated(db, 0); cil_set_attrs_expand_generated(db, 0);
f = xfopen(SELINUX_VERSION, "re"); f = xfopen(SELINUX_VERSION, "re");
fscanf(f, "%d", &policy_ver); fscanf(f, "%d", &policy_ver);
fclose(f); fclose(f);
cil_set_policy_version(db, policy_ver); cil_set_policy_version(db, policy_ver);
// Get mapping version // Get mapping version
f = xfopen(VEND_POLICY_DIR "plat_sepolicy_vers.txt", "re"); f = xfopen(VEND_POLICY_DIR "plat_sepolicy_vers.txt", "re");
fscanf(f, "%s", plat_ver); fscanf(f, "%s", plat_ver);
fclose(f); fclose(f);
// plat // plat
load_cil(db, SPLIT_PLAT_CIL); load_cil(db, SPLIT_PLAT_CIL);
sprintf(path, PLAT_POLICY_DIR "mapping/%s.cil", plat_ver); sprintf(path, PLAT_POLICY_DIR "mapping/%s.cil", plat_ver);
load_cil(db, path); load_cil(db, path);
sprintf(path, PLAT_POLICY_DIR "mapping/%s.compat.cil", plat_ver); sprintf(path, PLAT_POLICY_DIR "mapping/%s.compat.cil", plat_ver);
if (access(path, R_OK) == 0) if (access(path, R_OK) == 0)
load_cil(db, path); load_cil(db, path);
// system_ext // system_ext
sprintf(path, SYSEXT_POLICY_DIR "mapping/%s.cil", plat_ver); sprintf(path, SYSEXT_POLICY_DIR "mapping/%s.cil", plat_ver);
if (access(path, R_OK) == 0) if (access(path, R_OK) == 0)
load_cil(db, path); load_cil(db, path);
cil_file = SYSEXT_POLICY_DIR "system_ext_sepolicy.cil"; cil_file = SYSEXT_POLICY_DIR "system_ext_sepolicy.cil";
if (access(cil_file, R_OK) == 0) if (access(cil_file, R_OK) == 0)
load_cil(db, cil_file); load_cil(db, cil_file);
// product // product
sprintf(path, PROD_POLICY_DIR "mapping/%s.cil", plat_ver); sprintf(path, PROD_POLICY_DIR "mapping/%s.cil", plat_ver);
if (access(path, R_OK) == 0) if (access(path, R_OK) == 0)
load_cil(db, path); load_cil(db, path);
cil_file = PROD_POLICY_DIR "product_sepolicy.cil"; cil_file = PROD_POLICY_DIR "product_sepolicy.cil";
if (access(cil_file, R_OK) == 0) if (access(cil_file, R_OK) == 0)
load_cil(db, cil_file); load_cil(db, cil_file);
// vendor // vendor
cil_file = VEND_POLICY_DIR "nonplat_sepolicy.cil"; cil_file = VEND_POLICY_DIR "nonplat_sepolicy.cil";
if (access(cil_file, R_OK) == 0) if (access(cil_file, R_OK) == 0)
load_cil(db, cil_file); load_cil(db, cil_file);
cil_file = VEND_POLICY_DIR "plat_pub_versioned.cil"; cil_file = VEND_POLICY_DIR "plat_pub_versioned.cil";
if (access(cil_file, R_OK) == 0) if (access(cil_file, R_OK) == 0)
load_cil(db, cil_file); load_cil(db, cil_file);
cil_file = VEND_POLICY_DIR "vendor_sepolicy.cil"; cil_file = VEND_POLICY_DIR "vendor_sepolicy.cil";
if (access(cil_file, R_OK) == 0) if (access(cil_file, R_OK) == 0)
load_cil(db, cil_file); load_cil(db, cil_file);
// odm // odm
cil_file = ODM_POLICY_DIR "odm_sepolicy.cil"; cil_file = ODM_POLICY_DIR "odm_sepolicy.cil";
if (access(cil_file, R_OK) == 0) if (access(cil_file, R_OK) == 0)
load_cil(db, cil_file); load_cil(db, cil_file);
if (cil_compile(db)) if (cil_compile(db))
return nullptr; return nullptr;
if (cil_build_policydb(db, &pdb)) if (cil_build_policydb(db, &pdb))
return nullptr; return nullptr;
auto sepol = new sepolicy(); auto sepol = new sepolicy();
sepol->db = &pdb->p; sepol->db = &pdb->p;
return sepol; return sepol;
} }
sepolicy *sepolicy::from_split() { sepolicy *sepolicy::from_split() {
const char *odm_pre = ODM_POLICY_DIR "precompiled_sepolicy"; const char *odm_pre = ODM_POLICY_DIR "precompiled_sepolicy";
const char *vend_pre = VEND_POLICY_DIR "precompiled_sepolicy"; const char *vend_pre = VEND_POLICY_DIR "precompiled_sepolicy";
if (access(odm_pre, R_OK) == 0 && check_precompiled(odm_pre)) if (access(odm_pre, R_OK) == 0 && check_precompiled(odm_pre))
return sepolicy::from_file(odm_pre); return sepolicy::from_file(odm_pre);
else if (access(vend_pre, R_OK) == 0 && check_precompiled(vend_pre)) else if (access(vend_pre, R_OK) == 0 && check_precompiled(vend_pre))
return sepolicy::from_file(vend_pre); return sepolicy::from_file(vend_pre);
else else
return sepolicy::compile_split(); return sepolicy::compile_split();
} }
sepolicy::~sepolicy() { sepolicy::~sepolicy() {
policydb_destroy(db); policydb_destroy(db);
free(db); free(db);
} }
bool sepolicy::to_file(const char *file) { bool sepolicy::to_file(const char *file) {
uint8_t *data; uint8_t *data;
size_t len; size_t len;
/* No partial writes are allowed to /sys/fs/selinux/load, thus the reason why we /* No partial writes are allowed to /sys/fs/selinux/load, thus the reason why we
* first dump everything into memory, then directly call write system call */ * first dump everything into memory, then directly call write system call */
auto fp = make_stream_fp<byte_stream>(data, len); auto fp = make_stream_fp<byte_stream>(data, len);
run_finally fin([=]{ free(data); }); run_finally fin([=]{ free(data); });
policy_file_t pf; policy_file_t pf;
policy_file_init(&pf); policy_file_init(&pf);
pf.type = PF_USE_STDIO; pf.type = PF_USE_STDIO;
pf.fp = fp.get(); pf.fp = fp.get();
if (policydb_write(db, &pf)) { if (policydb_write(db, &pf)) {
LOGE("Fail to create policy image\n"); LOGE("Fail to create policy image\n");
return false; return false;
} }
int fd = xopen(file, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); int fd = xopen(file, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
if (fd < 0) if (fd < 0)
return false; return false;
xwrite(fd, data, len); xwrite(fd, data, len);
close(fd); close(fd);
return true; return true;
} }

View File

@@ -6,220 +6,220 @@
using namespace std; using namespace std;
void sepolicy::magisk_rules() { void sepolicy::magisk_rules() {
// Temp suppress warnings // Temp suppress warnings
auto bak = log_cb.w; auto bak = log_cb.w;
log_cb.w = nop_log; log_cb.w = nop_log;
// This indicates API 26+ // This indicates API 26+
bool new_rules = exists("untrusted_app_25"); bool new_rules = exists("untrusted_app_25");
// Prevent anything to change sepolicy except ourselves // Prevent anything to change sepolicy except ourselves
deny(ALL, "kernel", "security", "load_policy"); deny(ALL, "kernel", "security", "load_policy");
type(SEPOL_PROC_DOMAIN, "domain"); type(SEPOL_PROC_DOMAIN, "domain");
permissive(SEPOL_PROC_DOMAIN); /* Just in case something is missing */ permissive(SEPOL_PROC_DOMAIN); /* Just in case something is missing */
typeattribute(SEPOL_PROC_DOMAIN, "mlstrustedsubject"); typeattribute(SEPOL_PROC_DOMAIN, "mlstrustedsubject");
typeattribute(SEPOL_PROC_DOMAIN, "netdomain"); typeattribute(SEPOL_PROC_DOMAIN, "netdomain");
typeattribute(SEPOL_PROC_DOMAIN, "bluetoothdomain"); typeattribute(SEPOL_PROC_DOMAIN, "bluetoothdomain");
type(SEPOL_FILE_TYPE, "file_type"); type(SEPOL_FILE_TYPE, "file_type");
typeattribute(SEPOL_FILE_TYPE, "mlstrustedobject"); typeattribute(SEPOL_FILE_TYPE, "mlstrustedobject");
// Make our root domain unconstrained // Make our root domain unconstrained
allow(SEPOL_PROC_DOMAIN, ALL, ALL, ALL); allow(SEPOL_PROC_DOMAIN, ALL, ALL, ALL);
// Allow us to do any ioctl // Allow us to do any ioctl
if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) { if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) {
allowxperm(SEPOL_PROC_DOMAIN, ALL, "blk_file", ALL); allowxperm(SEPOL_PROC_DOMAIN, ALL, "blk_file", ALL);
allowxperm(SEPOL_PROC_DOMAIN, ALL, "fifo_file", ALL); allowxperm(SEPOL_PROC_DOMAIN, ALL, "fifo_file", ALL);
} }
// Create unconstrained file type // Create unconstrained file type
allow(ALL, SEPOL_FILE_TYPE, "file", ALL); allow(ALL, SEPOL_FILE_TYPE, "file", ALL);
allow(ALL, SEPOL_FILE_TYPE, "dir", ALL); allow(ALL, SEPOL_FILE_TYPE, "dir", ALL);
allow(ALL, SEPOL_FILE_TYPE, "fifo_file", ALL); allow(ALL, SEPOL_FILE_TYPE, "fifo_file", ALL);
allow(ALL, SEPOL_FILE_TYPE, "chr_file", ALL); allow(ALL, SEPOL_FILE_TYPE, "chr_file", ALL);
if (new_rules) { if (new_rules) {
// Make client type literally untrusted_app // Make client type literally untrusted_app
type(SEPOL_CLIENT_DOMAIN, "domain"); type(SEPOL_CLIENT_DOMAIN, "domain");
typeattribute(SEPOL_CLIENT_DOMAIN, "coredomain"); typeattribute(SEPOL_CLIENT_DOMAIN, "coredomain");
typeattribute(SEPOL_CLIENT_DOMAIN, "appdomain"); typeattribute(SEPOL_CLIENT_DOMAIN, "appdomain");
typeattribute(SEPOL_CLIENT_DOMAIN, "untrusted_app_all"); typeattribute(SEPOL_CLIENT_DOMAIN, "untrusted_app_all");
typeattribute(SEPOL_CLIENT_DOMAIN, "netdomain"); typeattribute(SEPOL_CLIENT_DOMAIN, "netdomain");
typeattribute(SEPOL_CLIENT_DOMAIN, "bluetoothdomain"); typeattribute(SEPOL_CLIENT_DOMAIN, "bluetoothdomain");
type(SEPOL_EXEC_TYPE, "file_type"); type(SEPOL_EXEC_TYPE, "file_type");
typeattribute(SEPOL_EXEC_TYPE, "exec_type"); typeattribute(SEPOL_EXEC_TYPE, "exec_type");
// Basic su client needs // Basic su client needs
allow(SEPOL_CLIENT_DOMAIN, SEPOL_EXEC_TYPE, "file", ALL); allow(SEPOL_CLIENT_DOMAIN, SEPOL_EXEC_TYPE, "file", ALL);
allow(SEPOL_CLIENT_DOMAIN, SEPOL_CLIENT_DOMAIN, ALL, ALL); allow(SEPOL_CLIENT_DOMAIN, SEPOL_CLIENT_DOMAIN, ALL, ALL);
const char *pts[] { const char *pts[] {
"devpts", "untrusted_app_devpts", "devpts", "untrusted_app_devpts",
"untrusted_app_25_devpts", "untrusted_app_all_devpts" }; "untrusted_app_25_devpts", "untrusted_app_all_devpts" };
for (auto type : pts) { for (auto type : pts) {
allow(SEPOL_CLIENT_DOMAIN, type, "chr_file", "open"); allow(SEPOL_CLIENT_DOMAIN, type, "chr_file", "open");
allow(SEPOL_CLIENT_DOMAIN, type, "chr_file", "getattr"); allow(SEPOL_CLIENT_DOMAIN, type, "chr_file", "getattr");
allow(SEPOL_CLIENT_DOMAIN, type, "chr_file", "read"); allow(SEPOL_CLIENT_DOMAIN, type, "chr_file", "read");
allow(SEPOL_CLIENT_DOMAIN, type, "chr_file", "write"); allow(SEPOL_CLIENT_DOMAIN, type, "chr_file", "write");
allow(SEPOL_CLIENT_DOMAIN, type, "chr_file", "ioctl"); allow(SEPOL_CLIENT_DOMAIN, type, "chr_file", "ioctl");
allowxperm(SEPOL_CLIENT_DOMAIN, type, "chr_file", "0x5400-0x54FF"); allowxperm(SEPOL_CLIENT_DOMAIN, type, "chr_file", "0x5400-0x54FF");
} }
// Allow these processes to access MagiskSU // Allow these processes to access MagiskSU
vector<const char *> clients{ "init", "shell", "update_engine", "appdomain" }; vector<const char *> clients{ "init", "shell", "update_engine", "appdomain" };
for (auto type : clients) { for (auto type : clients) {
if (!exists(type)) if (!exists(type))
continue; continue;
// exec magisk // exec magisk
allow(type, SEPOL_EXEC_TYPE, "file", "read"); allow(type, SEPOL_EXEC_TYPE, "file", "read");
allow(type, SEPOL_EXEC_TYPE, "file", "open"); allow(type, SEPOL_EXEC_TYPE, "file", "open");
allow(type, SEPOL_EXEC_TYPE, "file", "getattr"); allow(type, SEPOL_EXEC_TYPE, "file", "getattr");
allow(type, SEPOL_EXEC_TYPE, "file", "execute"); allow(type, SEPOL_EXEC_TYPE, "file", "execute");
allow(SEPOL_CLIENT_DOMAIN, type, "process", "sigchld"); allow(SEPOL_CLIENT_DOMAIN, type, "process", "sigchld");
// Auto transit to client domain // Auto transit to client domain
allow(type, SEPOL_CLIENT_DOMAIN, "process", "transition"); allow(type, SEPOL_CLIENT_DOMAIN, "process", "transition");
dontaudit(type, SEPOL_CLIENT_DOMAIN, "process", "siginh"); dontaudit(type, SEPOL_CLIENT_DOMAIN, "process", "siginh");
dontaudit(type, SEPOL_CLIENT_DOMAIN, "process", "rlimitinh"); dontaudit(type, SEPOL_CLIENT_DOMAIN, "process", "rlimitinh");
dontaudit(type, SEPOL_CLIENT_DOMAIN, "process", "noatsecure"); dontaudit(type, SEPOL_CLIENT_DOMAIN, "process", "noatsecure");
// Kill client process // Kill client process
allow(type, SEPOL_CLIENT_DOMAIN, "process", "signal"); allow(type, SEPOL_CLIENT_DOMAIN, "process", "signal");
} }
// type transition require actual types, not attributes // type transition require actual types, not attributes
const char *app_types[] { const char *app_types[] {
"system_app", "priv_app", "platform_app", "untrusted_app", "system_app", "priv_app", "platform_app", "untrusted_app",
"untrusted_app_25", "untrusted_app_27", "untrusted_app_29" }; "untrusted_app_25", "untrusted_app_27", "untrusted_app_29" };
clients.pop_back(); clients.pop_back();
clients.insert(clients.end(), app_types, app_types + std::size(app_types)); clients.insert(clients.end(), app_types, app_types + std::size(app_types));
for (auto type : clients) { for (auto type : clients) {
// Auto transit to client domain // Auto transit to client domain
type_transition(type, SEPOL_EXEC_TYPE, "process", SEPOL_CLIENT_DOMAIN); type_transition(type, SEPOL_EXEC_TYPE, "process", SEPOL_CLIENT_DOMAIN);
} }
// Allow system_server to manage magisk_client // Allow system_server to manage magisk_client
allow("system_server", SEPOL_CLIENT_DOMAIN, "process", "getpgid"); allow("system_server", SEPOL_CLIENT_DOMAIN, "process", "getpgid");
allow("system_server", SEPOL_CLIENT_DOMAIN, "process", "sigkill"); allow("system_server", SEPOL_CLIENT_DOMAIN, "process", "sigkill");
// Don't allow pesky processes to monitor audit deny logs when poking magisk daemon socket // Don't allow pesky processes to monitor audit deny logs when poking magisk daemon socket
dontaudit(ALL, SEPOL_PROC_DOMAIN, "unix_stream_socket", ALL); dontaudit(ALL, SEPOL_PROC_DOMAIN, "unix_stream_socket", ALL);
// Only allow client processes to connect to magisk daemon socket // Only allow client processes to connect to magisk daemon socket
allow(SEPOL_CLIENT_DOMAIN, SEPOL_PROC_DOMAIN, "unix_stream_socket", ALL); allow(SEPOL_CLIENT_DOMAIN, SEPOL_PROC_DOMAIN, "unix_stream_socket", ALL);
} else { } else {
// Fallback to poking holes in sandbox as Android 4.3 to 7.1 set PR_SET_NO_NEW_PRIVS // Fallback to poking holes in sandbox as Android 4.3 to 7.1 set PR_SET_NO_NEW_PRIVS
// Allow these processes to access MagiskSU // Allow these processes to access MagiskSU
const char *clients[] { "init", "shell", "appdomain" }; const char *clients[] { "init", "shell", "appdomain" };
for (auto type : clients) { for (auto type : clients) {
if (!exists(type)) if (!exists(type))
continue; continue;
allow(type, SEPOL_PROC_DOMAIN, "unix_stream_socket", "connectto"); allow(type, SEPOL_PROC_DOMAIN, "unix_stream_socket", "connectto");
allow(type, SEPOL_PROC_DOMAIN, "unix_stream_socket", "getopt"); allow(type, SEPOL_PROC_DOMAIN, "unix_stream_socket", "getopt");
// Allow termios ioctl // Allow termios ioctl
const char *pts[] { "devpts", "untrusted_app_devpts" }; const char *pts[] { "devpts", "untrusted_app_devpts" };
for (auto pts_type : pts) { for (auto pts_type : pts) {
allow(type, pts_type, "chr_file", "ioctl"); allow(type, pts_type, "chr_file", "ioctl");
if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL)
allowxperm(type, pts_type, "chr_file", "0x5400-0x54FF"); allowxperm(type, pts_type, "chr_file", "0x5400-0x54FF");
} }
} }
} }
// Let everyone access tmpfs files (for SAR sbin overlay) // Let everyone access tmpfs files (for SAR sbin overlay)
allow(ALL, "tmpfs", "file", ALL); allow(ALL, "tmpfs", "file", ALL);
// For relabelling files // For relabelling files
allow("rootfs", "labeledfs", "filesystem", "associate"); allow("rootfs", "labeledfs", "filesystem", "associate");
allow(SEPOL_FILE_TYPE, "pipefs", "filesystem", "associate"); allow(SEPOL_FILE_TYPE, "pipefs", "filesystem", "associate");
allow(SEPOL_FILE_TYPE, "devpts", "filesystem", "associate"); allow(SEPOL_FILE_TYPE, "devpts", "filesystem", "associate");
// Let init transit to SEPOL_PROC_DOMAIN // Let init transit to SEPOL_PROC_DOMAIN
allow("kernel", "kernel", "process", "setcurrent"); allow("kernel", "kernel", "process", "setcurrent");
allow("kernel", SEPOL_PROC_DOMAIN, "process", "dyntransition"); allow("kernel", SEPOL_PROC_DOMAIN, "process", "dyntransition");
// Let init run stuffs // Let init run stuffs
allow("kernel", SEPOL_PROC_DOMAIN, "fd", "use"); allow("kernel", SEPOL_PROC_DOMAIN, "fd", "use");
allow("init", SEPOL_PROC_DOMAIN, "process", ALL); allow("init", SEPOL_PROC_DOMAIN, "process", ALL);
allow("init", "tmpfs", "file", "getattr"); allow("init", "tmpfs", "file", "getattr");
allow("init", "tmpfs", "file", "execute"); allow("init", "tmpfs", "file", "execute");
// suRights // suRights
allow("servicemanager", SEPOL_PROC_DOMAIN, "dir", "search"); allow("servicemanager", SEPOL_PROC_DOMAIN, "dir", "search");
allow("servicemanager", SEPOL_PROC_DOMAIN, "dir", "read"); allow("servicemanager", SEPOL_PROC_DOMAIN, "dir", "read");
allow("servicemanager", SEPOL_PROC_DOMAIN, "file", "open"); allow("servicemanager", SEPOL_PROC_DOMAIN, "file", "open");
allow("servicemanager", SEPOL_PROC_DOMAIN, "file", "read"); allow("servicemanager", SEPOL_PROC_DOMAIN, "file", "read");
allow("servicemanager", SEPOL_PROC_DOMAIN, "process", "getattr"); allow("servicemanager", SEPOL_PROC_DOMAIN, "process", "getattr");
allow("servicemanager", SEPOL_PROC_DOMAIN, "binder", "transfer"); allow("servicemanager", SEPOL_PROC_DOMAIN, "binder", "transfer");
allow(ALL, SEPOL_PROC_DOMAIN, "process", "sigchld"); allow(ALL, SEPOL_PROC_DOMAIN, "process", "sigchld");
// allowLog // allowLog
allow("logd", SEPOL_PROC_DOMAIN, "dir", "search"); allow("logd", SEPOL_PROC_DOMAIN, "dir", "search");
allow("logd", SEPOL_PROC_DOMAIN, "file", "read"); allow("logd", SEPOL_PROC_DOMAIN, "file", "read");
allow("logd", SEPOL_PROC_DOMAIN, "file", "open"); allow("logd", SEPOL_PROC_DOMAIN, "file", "open");
allow("logd", SEPOL_PROC_DOMAIN, "file", "getattr"); allow("logd", SEPOL_PROC_DOMAIN, "file", "getattr");
// suBackL6 // suBackL6
allow("surfaceflinger", "app_data_file", "dir", ALL); allow("surfaceflinger", "app_data_file", "dir", ALL);
allow("surfaceflinger", "app_data_file", "file", ALL); allow("surfaceflinger", "app_data_file", "file", ALL);
allow("surfaceflinger", "app_data_file", "lnk_file", ALL); allow("surfaceflinger", "app_data_file", "lnk_file", ALL);
typeattribute("surfaceflinger", "mlstrustedsubject"); typeattribute("surfaceflinger", "mlstrustedsubject");
// suMiscL6 // suMiscL6
allow("audioserver", "audioserver", "process", "execmem"); allow("audioserver", "audioserver", "process", "execmem");
// Liveboot // Liveboot
allow("surfaceflinger", SEPOL_PROC_DOMAIN, "process", "ptrace"); allow("surfaceflinger", SEPOL_PROC_DOMAIN, "process", "ptrace");
allow("surfaceflinger", SEPOL_PROC_DOMAIN, "binder", "transfer"); allow("surfaceflinger", SEPOL_PROC_DOMAIN, "binder", "transfer");
allow("surfaceflinger", SEPOL_PROC_DOMAIN, "binder", "call"); allow("surfaceflinger", SEPOL_PROC_DOMAIN, "binder", "call");
allow("surfaceflinger", SEPOL_PROC_DOMAIN, "fd", "use"); allow("surfaceflinger", SEPOL_PROC_DOMAIN, "fd", "use");
allow("debuggerd", SEPOL_PROC_DOMAIN, "process", "ptrace"); allow("debuggerd", SEPOL_PROC_DOMAIN, "process", "ptrace");
// dumpsys // dumpsys
allow(ALL, SEPOL_PROC_DOMAIN, "fd", "use"); allow(ALL, SEPOL_PROC_DOMAIN, "fd", "use");
allow(ALL, SEPOL_PROC_DOMAIN, "fifo_file", "write"); allow(ALL, SEPOL_PROC_DOMAIN, "fifo_file", "write");
allow(ALL, SEPOL_PROC_DOMAIN, "fifo_file", "read"); allow(ALL, SEPOL_PROC_DOMAIN, "fifo_file", "read");
allow(ALL, SEPOL_PROC_DOMAIN, "fifo_file", "open"); allow(ALL, SEPOL_PROC_DOMAIN, "fifo_file", "open");
allow(ALL, SEPOL_PROC_DOMAIN, "fifo_file", "getattr"); allow(ALL, SEPOL_PROC_DOMAIN, "fifo_file", "getattr");
// bootctl // bootctl
allow("hwservicemanager", SEPOL_PROC_DOMAIN, "dir", "search"); allow("hwservicemanager", SEPOL_PROC_DOMAIN, "dir", "search");
allow("hwservicemanager", SEPOL_PROC_DOMAIN, "file", "read"); allow("hwservicemanager", SEPOL_PROC_DOMAIN, "file", "read");
allow("hwservicemanager", SEPOL_PROC_DOMAIN, "file", "open"); allow("hwservicemanager", SEPOL_PROC_DOMAIN, "file", "open");
allow("hwservicemanager", SEPOL_PROC_DOMAIN, "process", "getattr"); allow("hwservicemanager", SEPOL_PROC_DOMAIN, "process", "getattr");
allow("hwservicemanager", SEPOL_PROC_DOMAIN, "binder", "transfer"); allow("hwservicemanager", SEPOL_PROC_DOMAIN, "binder", "transfer");
// For mounting loop devices, mirrors, tmpfs // For mounting loop devices, mirrors, tmpfs
allow("kernel", ALL, "file", "read"); allow("kernel", ALL, "file", "read");
allow("kernel", ALL, "file", "write"); allow("kernel", ALL, "file", "write");
// Allow all binder transactions // Allow all binder transactions
allow(ALL, SEPOL_PROC_DOMAIN, "binder", ALL); allow(ALL, SEPOL_PROC_DOMAIN, "binder", ALL);
// For changing file context // For changing file context
allow("rootfs", "tmpfs", "filesystem", "associate"); allow("rootfs", "tmpfs", "filesystem", "associate");
// Xposed // Xposed
allow("untrusted_app", "untrusted_app", "capability", "setgid"); allow("untrusted_app", "untrusted_app", "capability", "setgid");
allow("system_server", "dex2oat_exec", "file", ALL); allow("system_server", "dex2oat_exec", "file", ALL);
// Support deodexed ROM on Oreo // Support deodexed ROM on Oreo
allow("zygote", "dalvikcache_data_file", "file", "execute"); allow("zygote", "dalvikcache_data_file", "file", "execute");
// Support deodexed ROM on Pie (Samsung) // Support deodexed ROM on Pie (Samsung)
allow("system_server", "dalvikcache_data_file", "file", "write"); allow("system_server", "dalvikcache_data_file", "file", "write");
allow("system_server", "dalvikcache_data_file", "file", "execute"); allow("system_server", "dalvikcache_data_file", "file", "execute");
// Allow update_engine/addon.d-v2 to run permissive on all ROMs // Allow update_engine/addon.d-v2 to run permissive on all ROMs
permissive("update_engine"); permissive("update_engine");
#if 0 #if 0
// Remove all dontaudit in debug mode // Remove all dontaudit in debug mode
impl->strip_dontaudit(); impl->strip_dontaudit();
#endif #endif
log_cb.w = bak; log_cb.w = bak;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -5,21 +5,21 @@
// Internal APIs, do not use directly // Internal APIs, do not use directly
struct sepol_impl : public sepolicy { struct sepol_impl : public sepolicy {
void check_avtab_node(avtab_ptr_t node); void check_avtab_node(avtab_ptr_t node);
avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xperms); avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xperms);
bool add_rule(const char *s, const char *t, const char *c, const char *p, int effect, bool invert); bool add_rule(const char *s, const char *t, const char *c, const char *p, int effect, bool invert);
void add_rule(type_datum_t *src, type_datum_t *tgt, class_datum_t *cls, perm_datum_t *perm, int effect, bool invert); void add_rule(type_datum_t *src, type_datum_t *tgt, class_datum_t *cls, perm_datum_t *perm, int effect, bool invert);
void add_xperm_rule(type_datum_t *src, type_datum_t *tgt, void add_xperm_rule(type_datum_t *src, type_datum_t *tgt,
class_datum_t *cls, uint16_t low, uint16_t high, int effect, bool invert); class_datum_t *cls, uint16_t low, uint16_t high, int effect, bool invert);
bool add_xperm_rule(const char *s, const char *t, const char *c, const char *range, int effect, bool invert); bool add_xperm_rule(const char *s, const char *t, const char *c, const char *range, int effect, bool invert);
bool add_type_rule(const char *s, const char *t, const char *c, const char *d, int effect); bool add_type_rule(const char *s, const char *t, const char *c, const char *d, int effect);
bool add_filename_trans(const char *s, const char *t, const char *c, const char *d, const char *o); bool add_filename_trans(const char *s, const char *t, const char *c, const char *d, const char *o);
bool add_genfscon(const char *fs_name, const char *path, const char *context); bool add_genfscon(const char *fs_name, const char *path, const char *context);
bool add_type(const char *type_name, uint32_t flavor); bool add_type(const char *type_name, uint32_t flavor);
bool set_type_state(const char *type_name, bool permissive); bool set_type_state(const char *type_name, bool permissive);
void add_typeattribute(type_datum_t *type, type_datum_t *attr); void add_typeattribute(type_datum_t *type, type_datum_t *attr);
bool add_typeattribute(const char *type, const char *attr); bool add_typeattribute(const char *type, const char *attr);
void strip_dontaudit(); void strip_dontaudit();
}; };
#define impl static_cast<sepol_impl *>(this) #define impl static_cast<sepol_impl *>(this)

View File

@@ -59,7 +59,7 @@ R"EOF("genfscon fs_name partial_path fs_context"
)EOF"; )EOF";
void statement_help() { void statement_help() {
fprintf(stderr, fprintf(stderr,
R"EOF(One policy statement should be treated as one parameter; R"EOF(One policy statement should be treated as one parameter;
this means each policy statement should be enclosed in quotes. this means each policy statement should be enclosed in quotes.
Multiple policy statements can be provided in a single command. Multiple policy statements can be provided in a single command.
@@ -91,65 +91,65 @@ Supported policy statements:
%s %s
)EOF", type_msg_1, type_msg_2, type_msg_3, type_msg_4, )EOF", type_msg_1, type_msg_2, type_msg_3, type_msg_4,
type_msg_5, type_msg_6, type_msg_7, type_msg_8, type_msg_9); type_msg_5, type_msg_6, type_msg_7, type_msg_8, type_msg_9);
exit(0); exit(0);
} }
using parsed_tokens = vector<vector<const char *>>; using parsed_tokens = vector<vector<const char *>>;
static bool tokenize_string(char *stmt, parsed_tokens &arr) { static bool tokenize_string(char *stmt, parsed_tokens &arr) {
// cur is the pointer to where the top level is parsing // cur is the pointer to where the top level is parsing
char *cur = stmt; char *cur = stmt;
for (char *tok; (tok = strtok_r(nullptr, " ", &cur)) != nullptr;) { for (char *tok; (tok = strtok_r(nullptr, " ", &cur)) != nullptr;) {
vector<const char *> token; vector<const char *> token;
if (tok[0] == '{') { if (tok[0] == '{') {
// cur could point to somewhere in the braces, restore the string // cur could point to somewhere in the braces, restore the string
if (cur) if (cur)
cur[-1] = ' '; cur[-1] = ' ';
++tok; ++tok;
char *end = strchr(tok, '}'); char *end = strchr(tok, '}');
if (end == nullptr) { if (end == nullptr) {
// Bracket not closed, syntax error // Bracket not closed, syntax error
return false; return false;
} }
*end = '\0'; *end = '\0';
for (char *sub_tok; (sub_tok = strtok_r(nullptr, " ", &tok)) != nullptr;) for (char *sub_tok; (sub_tok = strtok_r(nullptr, " ", &tok)) != nullptr;)
token.push_back(sub_tok); token.push_back(sub_tok);
cur = end + 1; cur = end + 1;
} else if (tok[0] == '*') { } else if (tok[0] == '*') {
token.push_back(nullptr); token.push_back(nullptr);
} else { } else {
token.push_back(tok); token.push_back(tok);
} }
arr.push_back(std::move(token)); arr.push_back(std::move(token));
} }
return true; return true;
} }
// Check array size and all args listed in 'ones' have size = 1 (no multiple entries) // Check array size and all args listed in 'ones' have size = 1 (no multiple entries)
template <int size, int ...ones> template <int size, int ...ones>
static bool check_tokens(parsed_tokens &arr) { static bool check_tokens(parsed_tokens &arr) {
if (arr.size() != size) if (arr.size() != size)
return false; return false;
initializer_list<int> list{ones...}; initializer_list<int> list{ones...};
for (int i : list) for (int i : list)
if (arr[i].size() != 1) if (arr[i].size() != 1)
return false; return false;
return true; return true;
} }
template <int size, int ...ones> template <int size, int ...ones>
static bool tokenize_and_check(char *stmt, parsed_tokens &arr) { static bool tokenize_and_check(char *stmt, parsed_tokens &arr) {
return tokenize_string(stmt, arr) && check_tokens<size, ones...>(arr); return tokenize_string(stmt, arr) && check_tokens<size, ones...>(arr);
} }
template <typename Func, typename ...Args> template <typename Func, typename ...Args>
static void run_and_check(const Func &fn, const char *action, Args ...args) { static void run_and_check(const Func &fn, const char *action, Args ...args) {
if (!fn(args...)) { if (!fn(args...)) {
string s = "Error in: %s"; string s = "Error in: %s";
for (int i = 0; i < sizeof...(args); ++i) s += " %s"; for (int i = 0; i < sizeof...(args); ++i) s += " %s";
s += "\n"; s += "\n";
LOGW(s.data(), action, (args ? args : "*")...); LOGW(s.data(), action, (args ? args : "*")...);
} }
} }
#define run_fn(...) run_and_check(fn, action, __VA_ARGS__) #define run_fn(...) run_and_check(fn, action, __VA_ARGS__)
@@ -157,166 +157,166 @@ static void run_and_check(const Func &fn, const char *action, Args ...args) {
// Pattern 1: allow { source } { target } { class } { permission } // Pattern 1: allow { source } { target } { class } { permission }
template <typename Func> template <typename Func>
static bool parse_pattern_1(const Func &fn, const char *action, char *stmt) { static bool parse_pattern_1(const Func &fn, const char *action, char *stmt) {
parsed_tokens arr; parsed_tokens arr;
if (!tokenize_and_check<4>(stmt, arr)) if (!tokenize_and_check<4>(stmt, arr))
return false; return false;
for (auto src : arr[0]) for (auto src : arr[0])
for (auto tgt : arr[1]) for (auto tgt : arr[1])
for (auto cls : arr[2]) for (auto cls : arr[2])
for (auto perm : arr[3]) for (auto perm : arr[3])
run_fn(src, tgt, cls, perm); run_fn(src, tgt, cls, perm);
return true; return true;
} }
// Pattern 2: allowxperm { source } { target } { class } ioctl range // Pattern 2: allowxperm { source } { target } { class } ioctl range
template <typename Func> template <typename Func>
static bool parse_pattern_2(const Func &fn, const char *action, char *stmt) { static bool parse_pattern_2(const Func &fn, const char *action, char *stmt) {
parsed_tokens arr; parsed_tokens arr;
if (!tokenize_and_check<5, 3, 4>(stmt, arr) || arr[3][0] != "ioctl"sv) if (!tokenize_and_check<5, 3, 4>(stmt, arr) || arr[3][0] != "ioctl"sv)
return false; return false;
auto range = arr[4][0]; auto range = arr[4][0];
for (auto src : arr[0]) for (auto src : arr[0])
for (auto tgt : arr[1]) for (auto tgt : arr[1])
for (auto cls : arr[2]) for (auto cls : arr[2])
run_fn(src, tgt, cls, range); run_fn(src, tgt, cls, range);
return true; return true;
} }
// Pattern 3: permissive { type } // Pattern 3: permissive { type }
template <typename Func> template <typename Func>
static bool parse_pattern_3(const Func &fn, const char *action, char *stmt) { static bool parse_pattern_3(const Func &fn, const char *action, char *stmt) {
parsed_tokens arr; parsed_tokens arr;
if (!tokenize_and_check<1>(stmt, arr)) if (!tokenize_and_check<1>(stmt, arr))
return false; return false;
for (auto type : arr[0]) for (auto type : arr[0])
run_fn(type); run_fn(type);
return true; return true;
} }
// Pattern 4: typeattribute { type } { attribute } // Pattern 4: typeattribute { type } { attribute }
template <typename Func> template <typename Func>
static bool parse_pattern_4(const Func &fn, const char *action, char *stmt) { static bool parse_pattern_4(const Func &fn, const char *action, char *stmt) {
parsed_tokens arr; parsed_tokens arr;
if (!tokenize_and_check<2>(stmt, arr)) if (!tokenize_and_check<2>(stmt, arr))
return false; return false;
for (auto type : arr[0]) for (auto type : arr[0])
for (auto attr : arr[1]) for (auto attr : arr[1])
run_fn(type, attr); run_fn(type, attr);
return true; return true;
} }
// Pattern 5: type name { attribute } // Pattern 5: type name { attribute }
template <typename Func> template <typename Func>
static bool parse_pattern_5(const Func &fn, const char *action, char *stmt) { static bool parse_pattern_5(const Func &fn, const char *action, char *stmt) {
parsed_tokens arr; parsed_tokens arr;
string tmp_str; string tmp_str;
if (!tokenize_string(stmt, arr)) if (!tokenize_string(stmt, arr))
return false; return false;
if (arr.size() == 1) { if (arr.size() == 1) {
arr.emplace_back(initializer_list<const char*>{ "domain" }); arr.emplace_back(initializer_list<const char*>{ "domain" });
} }
if (!check_tokens<2, 0>(arr)) if (!check_tokens<2, 0>(arr))
return false; return false;
for (auto attr : arr[1]) for (auto attr : arr[1])
run_fn(arr[0][0], attr); run_fn(arr[0][0], attr);
return true; return true;
} }
// Pattern 6: attribute name // Pattern 6: attribute name
template <typename Func> template <typename Func>
static bool parse_pattern_6(const Func &fn, const char *action, char *stmt) { static bool parse_pattern_6(const Func &fn, const char *action, char *stmt) {
parsed_tokens arr; parsed_tokens arr;
if (!tokenize_and_check<1, 0>(stmt, arr)) if (!tokenize_and_check<1, 0>(stmt, arr))
return false; return false;
run_fn(arr[0][1]); run_fn(arr[0][1]);
return true; return true;
} }
// Pattern 7: type_transition source target class default (filename) // Pattern 7: type_transition source target class default (filename)
template <typename Func> template <typename Func>
static bool parse_pattern_7(const Func &fn, const char *action, char *stmt) { static bool parse_pattern_7(const Func &fn, const char *action, char *stmt) {
parsed_tokens arr; parsed_tokens arr;
if (!tokenize_string(stmt, arr)) if (!tokenize_string(stmt, arr))
return false; return false;
if (arr.size() == 4) if (arr.size() == 4)
arr.emplace_back(initializer_list<const char*>{nullptr}); arr.emplace_back(initializer_list<const char*>{nullptr});
if (!check_tokens<5, 0, 1, 2, 3, 4>(arr)) if (!check_tokens<5, 0, 1, 2, 3, 4>(arr))
return false; return false;
run_fn(arr[0][0], arr[1][0], arr[2][0], arr[3][0], arr[4][0]); run_fn(arr[0][0], arr[1][0], arr[2][0], arr[3][0], arr[4][0]);
return true; return true;
} }
// Pattern 8: type_change source target class default // Pattern 8: type_change source target class default
template <typename Func> template <typename Func>
static bool parse_pattern_8(const Func &fn, const char *action, char *stmt) { static bool parse_pattern_8(const Func &fn, const char *action, char *stmt) {
parsed_tokens arr; parsed_tokens arr;
if (!tokenize_and_check<4, 0, 1, 2, 3>(stmt, arr)) if (!tokenize_and_check<4, 0, 1, 2, 3>(stmt, arr))
return false; return false;
run_fn(arr[0][0], arr[1][0], arr[2][0], arr[3][0]); run_fn(arr[0][0], arr[1][0], arr[2][0], arr[3][0]);
return true; return true;
} }
// Pattern 9: genfscon name path context // Pattern 9: genfscon name path context
template <typename Func> template <typename Func>
static bool parse_pattern_9(const Func &fn, const char *action, char *stmt) { static bool parse_pattern_9(const Func &fn, const char *action, char *stmt) {
parsed_tokens arr; parsed_tokens arr;
if (!tokenize_and_check<3, 0, 1, 2>(stmt, arr)) if (!tokenize_and_check<3, 0, 1, 2>(stmt, arr))
return false; return false;
run_fn(arr[0][0], arr[1][0], arr[2][0]); run_fn(arr[0][0], arr[1][0], arr[2][0]);
return true; return true;
} }
#define add_action_func(name, type, fn) \ #define add_action_func(name, type, fn) \
else if (strcmp(name, action) == 0) { \ else if (strcmp(name, action) == 0) { \
auto __fn = [=](auto && ...args){ return (fn)(args...); };\ auto __fn = [=](auto && ...args){ return (fn)(args...); };\
if (!parse_pattern_##type(__fn, name, remain)) \ if (!parse_pattern_##type(__fn, name, remain)) \
LOGW("Syntax error in '%s'\n\n%s\n", stmt, type_msg_##type); \ LOGW("Syntax error in '%s'\n\n%s\n", stmt, type_msg_##type); \
} }
#define add_action(act, type) add_action_func(#act, type, act) #define add_action(act, type) add_action_func(#act, type, act)
void sepolicy::parse_statement(const char *stmt) { void sepolicy::parse_statement(const char *stmt) {
// strtok modify strings, create a copy // strtok modify strings, create a copy
string cpy(stmt); string cpy(stmt);
char *remain; char *remain;
char *action = strtok_r(cpy.data(), " ", &remain); char *action = strtok_r(cpy.data(), " ", &remain);
if (remain == nullptr) { if (remain == nullptr) {
LOGW("Syntax error in '%s'\n\n", stmt); LOGW("Syntax error in '%s'\n\n", stmt);
return; return;
} }
if (0) {} if (0) {}
add_action(allow, 1) add_action(allow, 1)
add_action(deny, 1) add_action(deny, 1)
add_action(auditallow, 1) add_action(auditallow, 1)
add_action(dontaudit, 1) add_action(dontaudit, 1)
add_action(allowxperm, 2) add_action(allowxperm, 2)
add_action(auditallowxperm, 2) add_action(auditallowxperm, 2)
add_action(dontauditxperm, 2) add_action(dontauditxperm, 2)
add_action(permissive, 3) add_action(permissive, 3)
add_action(enforce, 3) add_action(enforce, 3)
add_action(typeattribute, 4) add_action(typeattribute, 4)
add_action(type, 5) add_action(type, 5)
add_action(attribute, 6) add_action(attribute, 6)
add_action(type_transition, 7) add_action(type_transition, 7)
add_action(type_change, 8) add_action(type_change, 8)
add_action(type_member, 8) add_action(type_member, 8)
add_action(genfscon, 9) add_action(genfscon, 9)
// Backwards compatible syntax // Backwards compatible syntax
add_action(create, 3) add_action(create, 3)
add_action_func("attradd", 4, typeattribute) add_action_func("attradd", 4, typeattribute)
add_action_func("name_transition", 7, type_transition) add_action_func("name_transition", 7, type_transition)
else { LOGW("Unknown action: '%s'\n\n", action); } else { LOGW("Unknown action: '%s'\n\n", action); }
} }
void sepolicy::load_rule_file(const char *file) { void sepolicy::load_rule_file(const char *file) {
file_readline(true, file, [=](string_view line) -> bool { file_readline(true, file, [=](string_view line) -> bool {
if (line.empty() || line[0] == '#') if (line.empty() || line[0] == '#')
return true; return true;
parse_statement(line.data()); parse_statement(line.data());
return true; return true;
}); });
} }

View File

@@ -8,12 +8,12 @@
#define PERSISTENT_PROPERTY_DIR "/data/property" #define PERSISTENT_PROPERTY_DIR "/data/property"
struct prop_cb { struct prop_cb {
virtual void exec(const char *name, const char *value) { virtual void exec(const char *name, const char *value) {
exec(std::string(name), value); exec(std::string(name), value);
} }
virtual void exec(std::string &&name, const char *value) { virtual void exec(std::string &&name, const char *value) {
exec(name.data(), value); exec(name.data(), value);
} }
}; };
extern bool use_pb; extern bool use_pb;
@@ -21,15 +21,15 @@ extern bool use_pb;
using prop_list = std::map<std::string, std::string>; using prop_list = std::map<std::string, std::string>;
struct prop_collector : prop_cb { struct prop_collector : prop_cb {
explicit prop_collector(prop_list &list) : list(list) {} explicit prop_collector(prop_list &list) : list(list) {}
void exec(const char *name, const char *value) override { void exec(const char *name, const char *value) override {
list.insert_or_assign(name, value); list.insert_or_assign(name, value);
} }
void exec(std::string &&name, const char *value) override { void exec(std::string &&name, const char *value) override {
list.insert_or_assign(std::move(name), value); list.insert_or_assign(std::move(name), value);
} }
private: private:
prop_list &list; prop_list &list;
}; };
std::string persist_getprop(const char *name); std::string persist_getprop(const char *name);

View File

@@ -23,13 +23,13 @@ using namespace std;
/* Struct definitions */ /* Struct definitions */
struct PersistentProperties { struct PersistentProperties {
pb_callback_t properties; pb_callback_t properties;
}; };
struct PersistentProperties_PersistentPropertyRecord { struct PersistentProperties_PersistentPropertyRecord {
pb_callback_t name; pb_callback_t name;
bool has_value; bool has_value;
char value[92]; char value[92];
}; };
/* Initializer values for message structs */ /* Initializer values for message structs */
@@ -78,163 +78,163 @@ PB_BIND(PersistentProperties_PersistentPropertyRecord, PersistentProperties_Pers
bool use_pb = false; bool use_pb = false;
static bool name_decode(pb_istream_t *stream, const pb_field_t *field, void **arg) { static bool name_decode(pb_istream_t *stream, const pb_field_t *field, void **arg) {
string &name = *static_cast<string *>(*arg); string &name = *static_cast<string *>(*arg);
name.resize(stream->bytes_left); name.resize(stream->bytes_left);
return pb_read(stream, (pb_byte_t *)(name.data()), stream->bytes_left); return pb_read(stream, (pb_byte_t *)(name.data()), stream->bytes_left);
} }
static bool name_encode(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) { static bool name_encode(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
return pb_encode_tag_for_field(stream, field) && return pb_encode_tag_for_field(stream, field) &&
pb_encode_string(stream, (const pb_byte_t *) *arg, strlen((const char *) *arg)); pb_encode_string(stream, (const pb_byte_t *) *arg, strlen((const char *) *arg));
} }
static bool prop_decode(pb_istream_t *stream, const pb_field_t *field, void **arg) { static bool prop_decode(pb_istream_t *stream, const pb_field_t *field, void **arg) {
PersistentProperties_PersistentPropertyRecord prop{}; PersistentProperties_PersistentPropertyRecord prop{};
string name; string name;
prop.name.funcs.decode = name_decode; prop.name.funcs.decode = name_decode;
prop.name.arg = &name; prop.name.arg = &name;
if (!pb_decode(stream, &PersistentProperties_PersistentPropertyRecord_msg, &prop)) if (!pb_decode(stream, &PersistentProperties_PersistentPropertyRecord_msg, &prop))
return false; return false;
auto cb = static_cast<prop_cb*>(*arg); auto cb = static_cast<prop_cb*>(*arg);
cb->exec(std::move(name), prop.value); cb->exec(std::move(name), prop.value);
return true; return true;
} }
static bool prop_encode(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) { static bool prop_encode(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
PersistentProperties_PersistentPropertyRecord prop{}; PersistentProperties_PersistentPropertyRecord prop{};
prop.name.funcs.encode = name_encode; prop.name.funcs.encode = name_encode;
prop.has_value = true; prop.has_value = true;
auto &list = *static_cast<prop_list *>(*arg); auto &list = *static_cast<prop_list *>(*arg);
for (auto &p : list) { for (auto &p : list) {
if (!pb_encode_tag_for_field(stream, field)) if (!pb_encode_tag_for_field(stream, field))
return false; return false;
prop.name.arg = (void *) p.first.data(); prop.name.arg = (void *) p.first.data();
strcpy(prop.value, p.second.data()); strcpy(prop.value, p.second.data());
if (!pb_encode_submessage(stream, &PersistentProperties_PersistentPropertyRecord_msg, &prop)) if (!pb_encode_submessage(stream, &PersistentProperties_PersistentPropertyRecord_msg, &prop))
return false; return false;
} }
return true; return true;
} }
static bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count) { static bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count) {
int fd = (intptr_t)stream->state; int fd = (intptr_t)stream->state;
return xwrite(fd, buf, count) == count; return xwrite(fd, buf, count) == count;
} }
static pb_ostream_t create_ostream(const char *filename) { static pb_ostream_t create_ostream(const char *filename) {
int fd = creat(filename, 0644); int fd = creat(filename, 0644);
pb_ostream_t o = { pb_ostream_t o = {
.callback = write_callback, .callback = write_callback,
.state = (void*)(intptr_t)fd, .state = (void*)(intptr_t)fd,
.max_size = SIZE_MAX, .max_size = SIZE_MAX,
.bytes_written = 0, .bytes_written = 0,
}; };
return o; return o;
} }
static void pb_getprop(prop_cb *prop_cb) { static void pb_getprop(prop_cb *prop_cb) {
LOGD("resetprop: decode with protobuf [" PERSISTENT_PROPERTY_DIR "/persistent_properties]\n"); LOGD("resetprop: decode with protobuf [" PERSISTENT_PROPERTY_DIR "/persistent_properties]\n");
PersistentProperties props = {}; PersistentProperties props = {};
props.properties.funcs.decode = prop_decode; props.properties.funcs.decode = prop_decode;
props.properties.arg = prop_cb; props.properties.arg = prop_cb;
pb_byte_t *buf; pb_byte_t *buf;
size_t size; size_t size;
mmap_ro(PERSISTENT_PROPERTY_DIR "/persistent_properties", buf, size); mmap_ro(PERSISTENT_PROPERTY_DIR "/persistent_properties", buf, size);
pb_istream_t stream = pb_istream_from_buffer(buf, size); pb_istream_t stream = pb_istream_from_buffer(buf, size);
pb_decode(&stream, &PersistentProperties_msg, &props); pb_decode(&stream, &PersistentProperties_msg, &props);
munmap(buf, size); munmap(buf, size);
} }
static bool file_getprop(const char *name, char *value) { static bool file_getprop(const char *name, char *value) {
char path[4096]; char path[4096];
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name); snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name);
int fd = open(path, O_RDONLY | O_CLOEXEC); int fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd < 0) if (fd < 0)
return false; return false;
LOGD("resetprop: read prop from [%s]\n", path); LOGD("resetprop: read prop from [%s]\n", path);
value[read(fd, value, PROP_VALUE_MAX - 1)] = '\0'; // Null terminate the read value value[read(fd, value, PROP_VALUE_MAX - 1)] = '\0'; // Null terminate the read value
close(fd); close(fd);
return value[0] != '\0'; return value[0] != '\0';
} }
void persist_getprops(prop_cb *prop_cb) { void persist_getprops(prop_cb *prop_cb) {
if (use_pb) { if (use_pb) {
pb_getprop(prop_cb); pb_getprop(prop_cb);
} else { } else {
auto dir = open_dir(PERSISTENT_PROPERTY_DIR); auto dir = open_dir(PERSISTENT_PROPERTY_DIR);
if (!dir) return; if (!dir) return;
for (dirent *entry; (entry = xreaddir(dir.get()));) { for (dirent *entry; (entry = xreaddir(dir.get()));) {
char value[PROP_VALUE_MAX]; char value[PROP_VALUE_MAX];
if (file_getprop(entry->d_name, value)) if (file_getprop(entry->d_name, value))
prop_cb->exec(entry->d_name, value); prop_cb->exec(entry->d_name, value);
} }
} }
} }
struct match_prop_name : prop_cb { struct match_prop_name : prop_cb {
explicit match_prop_name(const char *name) : _name(name) { value[0] = '\0'; } explicit match_prop_name(const char *name) : _name(name) { value[0] = '\0'; }
void exec(string &&name, const char *val) override { void exec(string &&name, const char *val) override {
if (name == _name) if (name == _name)
strcpy(value, val); strcpy(value, val);
} }
char value[PROP_VALUE_MAX]; char value[PROP_VALUE_MAX];
private: private:
const char *_name; const char *_name;
}; };
string persist_getprop(const char *name) { string persist_getprop(const char *name) {
if (use_pb) { if (use_pb) {
auto prop = match_prop_name(name); auto prop = match_prop_name(name);
pb_getprop(&prop); pb_getprop(&prop);
if (prop.value[0]) { if (prop.value[0]) {
LOGD("resetprop: getprop (persist) [%s]: [%s]\n", name, prop.value); LOGD("resetprop: getprop (persist) [%s]: [%s]\n", name, prop.value);
return prop.value; return prop.value;
} }
} else { } else {
// Try to read from file // Try to read from file
char value[PROP_VALUE_MAX]; char value[PROP_VALUE_MAX];
if (file_getprop(name, value)) { if (file_getprop(name, value)) {
LOGD("resetprop: getprop (persist) [%s]: [%s]\n", name, value); LOGD("resetprop: getprop (persist) [%s]: [%s]\n", name, value);
return value; return value;
} }
} }
return string(); return string();
} }
bool persist_deleteprop(const char *name) { bool persist_deleteprop(const char *name) {
if (use_pb) { if (use_pb) {
prop_list list; prop_list list;
prop_collector collector(list); prop_collector collector(list);
persist_getprops(&collector); persist_getprops(&collector);
for (auto it = list.begin(); it != list.end(); ++it) { for (auto it = list.begin(); it != list.end(); ++it) {
if (it->first == name) { if (it->first == name) {
list.erase(it); list.erase(it);
// Dump the props back // Dump the props back
PersistentProperties props{}; PersistentProperties props{};
pb_ostream_t ostream = create_ostream(PERSISTENT_PROPERTY_DIR pb_ostream_t ostream = create_ostream(PERSISTENT_PROPERTY_DIR
"/persistent_properties.tmp"); "/persistent_properties.tmp");
props.properties.funcs.encode = prop_encode; props.properties.funcs.encode = prop_encode;
props.properties.arg = &list; props.properties.arg = &list;
LOGD("resetprop: encode with protobuf [" PERSISTENT_PROPERTY_DIR LOGD("resetprop: encode with protobuf [" PERSISTENT_PROPERTY_DIR
"/persistent_properties.tmp]\n"); "/persistent_properties.tmp]\n");
if (!pb_encode(&ostream, &PersistentProperties_msg, &props)) if (!pb_encode(&ostream, &PersistentProperties_msg, &props))
return false; return false;
clone_attr(PERSISTENT_PROPERTY_DIR "/persistent_properties", clone_attr(PERSISTENT_PROPERTY_DIR "/persistent_properties",
PERSISTENT_PROPERTY_DIR "/persistent_properties.tmp"); PERSISTENT_PROPERTY_DIR "/persistent_properties.tmp");
rename(PERSISTENT_PROPERTY_DIR "/persistent_properties.tmp", rename(PERSISTENT_PROPERTY_DIR "/persistent_properties.tmp",
PERSISTENT_PROPERTY_DIR "/persistent_properties"); PERSISTENT_PROPERTY_DIR "/persistent_properties");
return true; return true;
} }
} }
return false; return false;
} else { } else {
char path[4096]; char path[4096];
snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name); snprintf(path, sizeof(path), PERSISTENT_PROPERTY_DIR "/%s", name);
if (unlink(path) == 0) { if (unlink(path) == 0) {
LOGD("resetprop: unlink [%s]\n", path); LOGD("resetprop: unlink [%s]\n", path);
return true; return true;
} }
} }
return false; return false;
} }

View File

@@ -26,24 +26,24 @@ static int (*system_property_set)(const char*, const char*);
static int (*system_property_read)(const prop_info*, char*, char*); static int (*system_property_read)(const prop_info*, char*, char*);
static const prop_info *(*system_property_find)(const char*); static const prop_info *(*system_property_find)(const char*);
static void (*system_property_read_callback)( static void (*system_property_read_callback)(
const prop_info*, void (*)(void*, const char*, const char*, uint32_t), void*); const prop_info*, void (*)(void*, const char*, const char*, uint32_t), void*);
static int (*system_property_foreach)(void (*)(const prop_info*, void*), void*); static int (*system_property_foreach)(void (*)(const prop_info*, void*), void*);
#define DLOAD(name) \ #define DLOAD(name) \
*(void **) &name = dlsym(RTLD_DEFAULT, "__" #name) *(void **) &name = dlsym(RTLD_DEFAULT, "__" #name)
static void load_functions() { static void load_functions() {
DLOAD(system_property_set); DLOAD(system_property_set);
DLOAD(system_property_read); DLOAD(system_property_read);
DLOAD(system_property_find); DLOAD(system_property_find);
DLOAD(system_property_read_callback); DLOAD(system_property_read_callback);
DLOAD(system_property_foreach); DLOAD(system_property_foreach);
} }
#undef DLOAD #undef DLOAD
#endif #endif
[[noreturn]] static void usage(char* arg0) { [[noreturn]] static void usage(char* arg0) {
fprintf(stderr, fprintf(stderr,
R"EOF(resetprop - System Property Manipulation Tool R"EOF(resetprop - System Property Manipulation Tool
Usage: %s [flags] [options...] Usage: %s [flags] [options...]
@@ -64,187 +64,187 @@ Flags:
(this flag only affects getprop and delprop) (this flag only affects getprop and delprop)
)EOF", arg0); )EOF", arg0);
exit(1); exit(1);
} }
static bool check_legal_property_name(const char *name) { static bool check_legal_property_name(const char *name) {
int namelen = strlen(name); int namelen = strlen(name);
if (namelen < 1) goto illegal; if (namelen < 1) goto illegal;
if (name[0] == '.') goto illegal; if (name[0] == '.') goto illegal;
if (name[namelen - 1] == '.') goto illegal; if (name[namelen - 1] == '.') goto illegal;
/* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */ /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
/* Don't allow ".." to appear in a property name */ /* Don't allow ".." to appear in a property name */
for (size_t i = 0; i < namelen; i++) { for (size_t i = 0; i < namelen; i++) {
if (name[i] == '.') { if (name[i] == '.') {
// i=0 is guaranteed to never have a dot. See above. // i=0 is guaranteed to never have a dot. See above.
if (name[i-1] == '.') goto illegal; if (name[i-1] == '.') goto illegal;
continue; continue;
} }
if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue; if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
if (name[i] >= 'a' && name[i] <= 'z') continue; if (name[i] >= 'a' && name[i] <= 'z') continue;
if (name[i] >= 'A' && name[i] <= 'Z') continue; if (name[i] >= 'A' && name[i] <= 'Z') continue;
if (name[i] >= '0' && name[i] <= '9') continue; if (name[i] >= '0' && name[i] <= '9') continue;
goto illegal; goto illegal;
} }
return true; return true;
illegal: illegal:
LOGE("Illegal property name: [%s]\n", name); LOGE("Illegal property name: [%s]\n", name);
return false; return false;
} }
static void read_prop(const prop_info *pi, void *cb) { static void read_prop(const prop_info *pi, void *cb) {
if (system_property_read_callback) { if (system_property_read_callback) {
auto callback = [](void *cb, const char *name, const char *value, uint32_t) { auto callback = [](void *cb, const char *name, const char *value, uint32_t) {
static_cast<prop_cb*>(cb)->exec(name, value); static_cast<prop_cb*>(cb)->exec(name, value);
}; };
system_property_read_callback(pi, callback, cb); system_property_read_callback(pi, callback, cb);
} else { } else {
char name[PROP_NAME_MAX]; char name[PROP_NAME_MAX];
char value[PROP_VALUE_MAX]; char value[PROP_VALUE_MAX];
name[0] = '\0'; name[0] = '\0';
value[0] = '\0'; value[0] = '\0';
system_property_read(pi, name, value); system_property_read(pi, name, value);
static_cast<prop_cb*>(cb)->exec(name, value); static_cast<prop_cb*>(cb)->exec(name, value);
} }
} }
struct sysprop_stub { struct sysprop_stub {
virtual int setprop(const char *name, const char *value, bool trigger) { return 1; } virtual int setprop(const char *name, const char *value, bool trigger) { return 1; }
virtual string getprop(const char *name, bool persist) { return string(); } virtual string getprop(const char *name, bool persist) { return string(); }
virtual void getprops(void (*callback)(const char *, const char *, void *), virtual void getprops(void (*callback)(const char *, const char *, void *),
void *cookie, bool persist) {} void *cookie, bool persist) {}
virtual int delprop(const char *name, bool persist) { return 1; } virtual int delprop(const char *name, bool persist) { return 1; }
}; };
struct sysprop : public sysprop_stub { struct sysprop : public sysprop_stub {
int setprop(const char *name, const char *value, bool) override { int setprop(const char *name, const char *value, bool) override {
if (!check_legal_property_name(name)) if (!check_legal_property_name(name))
return 1; return 1;
return system_property_set(name, value); return system_property_set(name, value);
} }
struct prop_to_string : prop_cb { struct prop_to_string : prop_cb {
explicit prop_to_string(string &s) : val(s) {} explicit prop_to_string(string &s) : val(s) {}
void exec(const char *, const char *value) override { void exec(const char *, const char *value) override {
val = value; val = value;
} }
private: private:
string &val; string &val;
}; };
string getprop(const char *name, bool) override { string getprop(const char *name, bool) override {
string val; string val;
if (!check_legal_property_name(name)) if (!check_legal_property_name(name))
return val; return val;
auto pi = system_property_find(name); auto pi = system_property_find(name);
if (pi == nullptr) if (pi == nullptr)
return val; return val;
auto prop = prop_to_string(val); auto prop = prop_to_string(val);
read_prop(pi, &prop); read_prop(pi, &prop);
LOGD("resetprop: getprop [%s]: [%s]\n", name, val.data()); LOGD("resetprop: getprop [%s]: [%s]\n", name, val.data());
return val; return val;
} }
void getprops(void (*callback)(const char*, const char*, void*), void *cookie, bool) override { void getprops(void (*callback)(const char*, const char*, void*), void *cookie, bool) override {
prop_list list; prop_list list;
prop_collector collector(list); prop_collector collector(list);
system_property_foreach(read_prop, &collector); system_property_foreach(read_prop, &collector);
for (auto &[key, val] : list) for (auto &[key, val] : list)
callback(key.data(), val.data(), cookie); callback(key.data(), val.data(), cookie);
} }
}; };
struct resetprop : public sysprop { struct resetprop : public sysprop {
int setprop(const char *name, const char *value, bool prop_svc) override { int setprop(const char *name, const char *value, bool prop_svc) override {
if (!check_legal_property_name(name)) if (!check_legal_property_name(name))
return 1; return 1;
const char *msg = prop_svc ? "property_service" : "modifying prop data structure"; const char *msg = prop_svc ? "property_service" : "modifying prop data structure";
int ret; int ret;
auto pi = const_cast<prop_info *>(__system_property_find(name)); auto pi = const_cast<prop_info *>(__system_property_find(name));
if (pi != nullptr) { if (pi != nullptr) {
if (prop_svc) { if (prop_svc) {
if (strncmp(name, "ro.", 3) == 0) if (strncmp(name, "ro.", 3) == 0)
delprop(name, false); delprop(name, false);
ret = system_property_set(name, value); ret = system_property_set(name, value);
} else { } else {
ret = __system_property_update(pi, value, strlen(value)); ret = __system_property_update(pi, value, strlen(value));
} }
LOGD("resetprop: update prop [%s]: [%s] by %s\n", name, value, msg); LOGD("resetprop: update prop [%s]: [%s] by %s\n", name, value, msg);
} else { } else {
if (prop_svc) { if (prop_svc) {
ret = system_property_set(name, value); ret = system_property_set(name, value);
} else { } else {
ret = __system_property_add(name, strlen(name), value, strlen(value)); ret = __system_property_add(name, strlen(name), value, strlen(value));
} }
LOGD("resetprop: create prop [%s]: [%s] by %s\n", name, value, msg); LOGD("resetprop: create prop [%s]: [%s] by %s\n", name, value, msg);
} }
if (ret) if (ret)
LOGW("resetprop: setprop error\n"); LOGW("resetprop: setprop error\n");
return ret; return ret;
} }
string getprop(const char *name, bool persist) override { string getprop(const char *name, bool persist) override {
string val = sysprop::getprop(name, persist); string val = sysprop::getprop(name, persist);
if (val.empty() && persist && strncmp(name, "persist.", 8) == 0) if (val.empty() && persist && strncmp(name, "persist.", 8) == 0)
val = persist_getprop(name); val = persist_getprop(name);
if (val.empty()) if (val.empty())
LOGD("resetprop: prop [%s] does not exist\n", name); LOGD("resetprop: prop [%s] does not exist\n", name);
return val; return val;
} }
void getprops(void (*callback)(const char *, const char *, void *), void getprops(void (*callback)(const char *, const char *, void *),
void *cookie, bool persist) override { void *cookie, bool persist) override {
prop_list list; prop_list list;
prop_collector collector(list); prop_collector collector(list);
system_property_foreach(read_prop, &collector); system_property_foreach(read_prop, &collector);
if (persist) if (persist)
persist_getprops(&collector); persist_getprops(&collector);
for (auto &[key, val] : list) for (auto &[key, val] : list)
callback(key.data(), val.data(), cookie); callback(key.data(), val.data(), cookie);
} }
int delprop(const char *name, bool persist) override { int delprop(const char *name, bool persist) override {
if (!check_legal_property_name(name)) if (!check_legal_property_name(name))
return 1; return 1;
LOGD("resetprop: delete prop [%s]\n", name); LOGD("resetprop: delete prop [%s]\n", name);
if (persist && strncmp(name, "persist.", 8) == 0) if (persist && strncmp(name, "persist.", 8) == 0)
persist = persist_deleteprop(name); persist = persist_deleteprop(name);
return __system_property_delete(name) && !(persist && strncmp(name, "persist.", 8) == 0); return __system_property_delete(name) && !(persist && strncmp(name, "persist.", 8) == 0);
} }
}; };
static sysprop_stub *get_impl() { static sysprop_stub *get_impl() {
static sysprop_stub *impl = nullptr; static sysprop_stub *impl = nullptr;
if (impl == nullptr) { if (impl == nullptr) {
use_pb = access(PERSISTENT_PROPERTY_DIR "/persistent_properties", R_OK) == 0; use_pb = access(PERSISTENT_PROPERTY_DIR "/persistent_properties", R_OK) == 0;
#ifdef APPLET_STUB_MAIN #ifdef APPLET_STUB_MAIN
if (__system_properties_init()) { if (__system_properties_init()) {
LOGE("resetprop: __system_properties_init error\n"); LOGE("resetprop: __system_properties_init error\n");
exit(1); exit(1);
} }
impl = new resetprop(); impl = new resetprop();
#else #else
// Load platform implementations // Load platform implementations
load_functions(); load_functions();
if (__system_properties_init()) { if (__system_properties_init()) {
LOGW("resetprop: __system_properties_init error\n"); LOGW("resetprop: __system_properties_init error\n");
impl = new sysprop(); impl = new sysprop();
} else { } else {
impl = new resetprop(); impl = new resetprop();
} }
#endif #endif
} }
return impl; return impl;
} }
/*********************************** /***********************************
@@ -252,94 +252,94 @@ static sysprop_stub *get_impl() {
***********************************/ ***********************************/
static void print_props(bool persist) { static void print_props(bool persist) {
getprops([](const char *name, const char *value, auto) { getprops([](const char *name, const char *value, auto) {
printf("[%s]: [%s]\n", name, value); printf("[%s]: [%s]\n", name, value);
}, nullptr, persist); }, nullptr, persist);
} }
string getprop(const char *name, bool persist) { string getprop(const char *name, bool persist) {
return get_impl()->getprop(name, persist); return get_impl()->getprop(name, persist);
} }
void getprops(void (*callback)(const char *, const char *, void *), void *cookie, bool persist) { void getprops(void (*callback)(const char *, const char *, void *), void *cookie, bool persist) {
get_impl()->getprops(callback, cookie, persist); get_impl()->getprops(callback, cookie, persist);
} }
int setprop(const char *name, const char *value, bool prop_svc) { int setprop(const char *name, const char *value, bool prop_svc) {
return get_impl()->setprop(name, value, prop_svc); return get_impl()->setprop(name, value, prop_svc);
} }
int delprop(const char *name, bool persist) { int delprop(const char *name, bool persist) {
return get_impl()->delprop(name, persist); return get_impl()->delprop(name, persist);
} }
void load_prop_file(const char *filename, bool prop_svc) { void load_prop_file(const char *filename, bool prop_svc) {
auto impl = get_impl(); auto impl = get_impl();
LOGD("resetprop: Parse prop file [%s]\n", filename); LOGD("resetprop: Parse prop file [%s]\n", filename);
parse_prop_file(filename, [=](auto key, auto val) -> bool { parse_prop_file(filename, [=](auto key, auto val) -> bool {
impl->setprop(key.data(), val.data(), prop_svc); impl->setprop(key.data(), val.data(), prop_svc);
return true; return true;
}); });
} }
int resetprop_main(int argc, char *argv[]) { int resetprop_main(int argc, char *argv[]) {
log_cb.d = [](auto fmt, auto ap) -> int { return verbose ? vfprintf(stderr, fmt, ap) : 0; }; log_cb.d = [](auto fmt, auto ap) -> int { return verbose ? vfprintf(stderr, fmt, ap) : 0; };
bool prop_svc = true; bool prop_svc = true;
bool persist = false; bool persist = false;
char *argv0 = argv[0]; char *argv0 = argv[0];
--argc; --argc;
++argv; ++argv;
// Parse flags and -- options // Parse flags and -- options
while (argc && argv[0][0] == '-') { while (argc && argv[0][0] == '-') {
for (int idx = 1; true; ++idx) { for (int idx = 1; true; ++idx) {
switch (argv[0][idx]) { switch (argv[0][idx]) {
case '-': case '-':
if (strcmp(argv[0], "--file") == 0 && argc == 2) { if (strcmp(argv[0], "--file") == 0 && argc == 2) {
load_prop_file(argv[1], prop_svc); load_prop_file(argv[1], prop_svc);
return 0; return 0;
} else if (strcmp(argv[0], "--delete") == 0 && argc == 2) { } else if (strcmp(argv[0], "--delete") == 0 && argc == 2) {
return delprop(argv[1], persist); return delprop(argv[1], persist);
} else if (strcmp(argv[0], "--help") == 0) { } else if (strcmp(argv[0], "--help") == 0) {
usage(argv0); usage(argv0);
} }
case 'v': case 'v':
verbose = true; verbose = true;
continue; continue;
case 'p': case 'p':
persist = true; persist = true;
continue; continue;
case 'n': case 'n':
prop_svc = false; prop_svc = false;
continue; continue;
case '\0': case '\0':
break; break;
case 'h': case 'h':
default: default:
usage(argv0); usage(argv0);
} }
break; break;
} }
--argc; --argc;
++argv; ++argv;
} }
switch (argc) { switch (argc) {
case 0: case 0:
print_props(persist); print_props(persist);
return 0; return 0;
case 1: { case 1: {
string prop = getprop(argv[0], persist); string prop = getprop(argv[0], persist);
if (prop.empty()) if (prop.empty())
return 1; return 1;
printf("%s\n", prop.data()); printf("%s\n", prop.data());
return 0; return 0;
} }
case 2: case 2:
return setprop(argv[0], argv[1], prop_svc); return setprop(argv[0], argv[1], prop_svc);
default: default:
usage(argv0); usage(argv0);
} }
} }

View File

@@ -10,9 +10,9 @@
using namespace std; using namespace std;
enum { enum {
NAMED_ACTIVITY, NAMED_ACTIVITY,
PKG_ACTIVITY, PKG_ACTIVITY,
CONTENT_PROVIDER CONTENT_PROVIDER
}; };
#define CALL_PROVIDER \ #define CALL_PROVIDER \
@@ -34,179 +34,179 @@ enum {
(to.command[0] ? to.command : to.shell[0] ? to.shell : DEFAULT_SHELL) (to.command[0] ? to.command : to.shell[0] ? to.shell : DEFAULT_SHELL)
class Extra { class Extra {
const char *key; const char *key;
enum { enum {
INT, INT,
BOOL, BOOL,
STRING STRING
} type; } type;
union { union {
int int_val; int int_val;
bool bool_val; bool bool_val;
const char * str_val; const char * str_val;
}; };
char buf[32]; char buf[32];
public: public:
Extra(const char *k, int v): key(k), type(INT), int_val(v) {} Extra(const char *k, int v): key(k), type(INT), int_val(v) {}
Extra(const char *k, bool v): key(k), type(BOOL), bool_val(v) {} Extra(const char *k, bool v): key(k), type(BOOL), bool_val(v) {}
Extra(const char *k, const char *v): key(k), type(STRING), str_val(v) {} Extra(const char *k, const char *v): key(k), type(STRING), str_val(v) {}
void add_intent(vector<const char *> &vec) { void add_intent(vector<const char *> &vec) {
const char *val; const char *val;
switch (type) { switch (type) {
case INT: case INT:
vec.push_back("--ei"); vec.push_back("--ei");
sprintf(buf, "%d", int_val); sprintf(buf, "%d", int_val);
val = buf; val = buf;
break; break;
case BOOL: case BOOL:
vec.push_back("--ez"); vec.push_back("--ez");
val = bool_val ? "true" : "false"; val = bool_val ? "true" : "false";
break; break;
case STRING: case STRING:
vec.push_back("--es"); vec.push_back("--es");
val = str_val; val = str_val;
break; break;
} }
vec.push_back(key); vec.push_back(key);
vec.push_back(val); vec.push_back(val);
} }
void add_bind(vector<const char *> &vec) { void add_bind(vector<const char *> &vec) {
switch (type) { switch (type) {
case INT: case INT:
sprintf(buf, "%s:i:%d", key, int_val); sprintf(buf, "%s:i:%d", key, int_val);
break; break;
case BOOL: case BOOL:
sprintf(buf, "%s:b:%s", key, bool_val ? "true" : "false"); sprintf(buf, "%s:b:%s", key, bool_val ? "true" : "false");
break; break;
case STRING: case STRING:
sprintf(buf, "%s:s:%s", key, str_val); sprintf(buf, "%s:s:%s", key, str_val);
break; break;
} }
vec.push_back("--extra"); vec.push_back("--extra");
vec.push_back(buf); vec.push_back(buf);
} }
}; };
static bool check_no_error(int fd) { static bool check_no_error(int fd) {
char buf[1024]; char buf[1024];
auto out = xopen_file(fd, "r"); auto out = xopen_file(fd, "r");
while (fgets(buf, sizeof(buf), out.get())) { while (fgets(buf, sizeof(buf), out.get())) {
if (strncmp(buf, "Error", 5) == 0) if (strncmp(buf, "Error", 5) == 0)
return false; return false;
} }
return true; return true;
} }
static void exec_cmd(const char *action, vector<Extra> &data, static void exec_cmd(const char *action, vector<Extra> &data,
const shared_ptr<su_info> &info, int mode = CONTENT_PROVIDER) { const shared_ptr<su_info> &info, int mode = CONTENT_PROVIDER) {
char target[128]; char target[128];
char user[4]; char user[4];
sprintf(user, "%d", get_user(info)); sprintf(user, "%d", get_user(info));
// First try content provider call method // First try content provider call method
if (mode >= CONTENT_PROVIDER) { if (mode >= CONTENT_PROVIDER) {
sprintf(target, "content://%s.provider", info->str[SU_MANAGER].data()); sprintf(target, "content://%s.provider", info->str[SU_MANAGER].data());
vector<const char *> args{ CALL_PROVIDER }; vector<const char *> args{ CALL_PROVIDER };
for (auto &e : data) { for (auto &e : data) {
e.add_bind(args); e.add_bind(args);
} }
args.push_back(nullptr); args.push_back(nullptr);
exec_t exec { exec_t exec {
.err = true, .err = true,
.fd = -1, .fd = -1,
.pre_exec = [] { setenv("CLASSPATH", "/system/framework/content.jar", 1); }, .pre_exec = [] { setenv("CLASSPATH", "/system/framework/content.jar", 1); },
.argv = args.data() .argv = args.data()
}; };
exec_command_sync(exec); exec_command_sync(exec);
if (check_no_error(exec.fd)) if (check_no_error(exec.fd))
return; return;
} }
vector<const char *> args{ START_ACTIVITY }; vector<const char *> args{ START_ACTIVITY };
for (auto &e : data) { for (auto &e : data) {
e.add_intent(args); e.add_intent(args);
} }
args.push_back(nullptr); args.push_back(nullptr);
exec_t exec { exec_t exec {
.err = true, .err = true,
.fd = -1, .fd = -1,
.pre_exec = [] { setenv("CLASSPATH", "/system/framework/am.jar", 1); }, .pre_exec = [] { setenv("CLASSPATH", "/system/framework/am.jar", 1); },
.argv = args.data() .argv = args.data()
}; };
if (mode >= PKG_ACTIVITY) { if (mode >= PKG_ACTIVITY) {
// Then try start activity without component name // Then try start activity without component name
strcpy(target, info->str[SU_MANAGER].data()); strcpy(target, info->str[SU_MANAGER].data());
exec_command_sync(exec); exec_command_sync(exec);
if (check_no_error(exec.fd)) if (check_no_error(exec.fd))
return; return;
} }
// Finally, fallback to start activity with component name // Finally, fallback to start activity with component name
args[4] = "-n"; args[4] = "-n";
sprintf(target, "%s/a.m", info->str[SU_MANAGER].data()); sprintf(target, "%s/a.m", info->str[SU_MANAGER].data());
exec.fd = -2; exec.fd = -2;
exec.fork = fork_dont_care; exec.fork = fork_dont_care;
exec_command(exec); exec_command(exec);
} }
void app_log(const su_context &ctx) { void app_log(const su_context &ctx) {
if (fork_dont_care() == 0) { if (fork_dont_care() == 0) {
vector<Extra> extras; vector<Extra> extras;
extras.reserve(6); extras.reserve(6);
extras.emplace_back("from.uid", ctx.info->uid); extras.emplace_back("from.uid", ctx.info->uid);
extras.emplace_back("to.uid", ctx.req.uid); extras.emplace_back("to.uid", ctx.req.uid);
extras.emplace_back("pid", ctx.pid); extras.emplace_back("pid", ctx.pid);
extras.emplace_back("policy", ctx.info->access.policy); extras.emplace_back("policy", ctx.info->access.policy);
extras.emplace_back("command", get_cmd(ctx.req)); extras.emplace_back("command", get_cmd(ctx.req));
extras.emplace_back("notify", (bool) ctx.info->access.notify); extras.emplace_back("notify", (bool) ctx.info->access.notify);
exec_cmd("log", extras, ctx.info); exec_cmd("log", extras, ctx.info);
exit(0); exit(0);
} }
} }
void app_notify(const su_context &ctx) { void app_notify(const su_context &ctx) {
if (fork_dont_care() == 0) { if (fork_dont_care() == 0) {
vector<Extra> extras; vector<Extra> extras;
extras.reserve(2); extras.reserve(2);
extras.emplace_back("from.uid", ctx.info->uid); extras.emplace_back("from.uid", ctx.info->uid);
extras.emplace_back("policy", ctx.info->access.policy); extras.emplace_back("policy", ctx.info->access.policy);
exec_cmd("notify", extras, ctx.info); exec_cmd("notify", extras, ctx.info);
exit(0); exit(0);
} }
} }
int app_request(const shared_ptr<su_info> &info) { int app_request(const shared_ptr<su_info> &info) {
// Create FIFO // Create FIFO
char fifo[64]; char fifo[64];
strcpy(fifo, "/dev/socket/"); strcpy(fifo, "/dev/socket/");
gen_rand_str(fifo + 12, 32, true); gen_rand_str(fifo + 12, 32, true);
mkfifo(fifo, 0600); mkfifo(fifo, 0600);
chown(fifo, info->mgr_st.st_uid, info->mgr_st.st_gid); chown(fifo, info->mgr_st.st_uid, info->mgr_st.st_gid);
setfilecon(fifo, "u:object_r:" SEPOL_FILE_TYPE ":s0"); setfilecon(fifo, "u:object_r:" SEPOL_FILE_TYPE ":s0");
// Send request // Send request
vector<Extra> extras; vector<Extra> extras;
extras.reserve(2); extras.reserve(2);
extras.emplace_back("fifo", fifo); extras.emplace_back("fifo", fifo);
extras.emplace_back("uid", info->uid); extras.emplace_back("uid", info->uid);
exec_cmd("request", extras, info, PKG_ACTIVITY); exec_cmd("request", extras, info, PKG_ACTIVITY);
// Wait for data input for at most 70 seconds // Wait for data input for at most 70 seconds
int fd = xopen(fifo, O_RDONLY | O_CLOEXEC); int fd = xopen(fifo, O_RDONLY | O_CLOEXEC);
struct pollfd pfd = { struct pollfd pfd = {
.fd = fd, .fd = fd,
.events = POLL_IN .events = POLL_IN
}; };
if (xpoll(&pfd, 1, 70 * 1000) <= 0) { if (xpoll(&pfd, 1, 70 * 1000) <= 0) {
close(fd); close(fd);
fd = -1; fd = -1;
} }
unlink(fifo); unlink(fifo);
return fd; return fd;
} }

View File

@@ -26,16 +26,16 @@
*/ */
// Ensures all the data is written out // Ensures all the data is written out
static int write_blocking(int fd, char *buf, ssize_t bufsz) { static int write_blocking(int fd, char *buf, ssize_t bufsz) {
ssize_t ret, written; ssize_t ret, written;
written = 0; written = 0;
do { do {
ret = write(fd, buf + written, bufsz - written); ret = write(fd, buf + written, bufsz - written);
if (ret == -1) return -1; if (ret == -1) return -1;
written += ret; written += ret;
} while (written < bufsz); } while (written < bufsz);
return 0; return 0;
} }
/** /**
@@ -43,28 +43,28 @@ static int write_blocking(int fd, char *buf, ssize_t bufsz) {
* true, then close the output FD when we're done. * true, then close the output FD when we're done.
*/ */
static void pump(int input, int output, bool close_output = true) { static void pump(int input, int output, bool close_output = true) {
char buf[4096]; char buf[4096];
int len; int len;
while ((len = read(input, buf, 4096)) > 0) { while ((len = read(input, buf, 4096)) > 0) {
if (write_blocking(output, buf, len) == -1) break; if (write_blocking(output, buf, len) == -1) break;
} }
close(input); close(input);
if (close_output) close(output); if (close_output) close(output);
} }
static void* pump_thread(void* data) { static void* pump_thread(void* data) {
int *fds = (int*) data; int *fds = (int*) data;
pump(fds[0], fds[1]); pump(fds[0], fds[1]);
delete[] fds; delete[] fds;
return nullptr; return nullptr;
} }
static void pump_async(int input, int output) { static void pump_async(int input, int output) {
pthread_t writer; pthread_t writer;
int *fds = new int[2]; int *fds = new int[2];
fds[0] = input; fds[0] = input;
fds[1] = output; fds[1] = output;
pthread_create(&writer, nullptr, pump_thread, fds); pthread_create(&writer, nullptr, pump_thread, fds);
} }
@@ -82,31 +82,31 @@ static void pump_async(int input, int output) {
* on success, the file descriptor of the master device is returned. * on success, the file descriptor of the master device is returned.
*/ */
int pts_open(char *slave_name, size_t slave_name_size) { int pts_open(char *slave_name, size_t slave_name_size) {
int fdm; int fdm;
// Open master ptmx device // Open master ptmx device
fdm = open("/dev/ptmx", O_RDWR); fdm = open("/dev/ptmx", O_RDWR);
if (fdm == -1) if (fdm == -1)
goto error; goto error;
// Get the slave name // Get the slave name
if (ptsname_r(fdm, slave_name, slave_name_size-1)) if (ptsname_r(fdm, slave_name, slave_name_size-1))
goto error; goto error;
slave_name[slave_name_size - 1] = '\0'; slave_name[slave_name_size - 1] = '\0';
// Grant, then unlock // Grant, then unlock
if (grantpt(fdm) == -1) if (grantpt(fdm) == -1)
goto error; goto error;
if (unlockpt(fdm) == -1) if (unlockpt(fdm) == -1)
goto error; goto error;
return fdm; return fdm;
error: error:
close(fdm); close(fdm);
PLOGE("pts_open"); PLOGE("pts_open");
return -1; return -1;
} }
// Stores the previous termios of stdin // Stores the previous termios of stdin
@@ -124,31 +124,31 @@ static int stdin_is_raw = 0;
* on success 0 * on success 0
*/ */
int set_stdin_raw(void) { int set_stdin_raw(void) {
struct termios new_termios; struct termios new_termios;
// Save the current stdin termios // Save the current stdin termios
if (tcgetattr(STDIN_FILENO, &old_stdin) < 0) { if (tcgetattr(STDIN_FILENO, &old_stdin) < 0) {
return -1; return -1;
} }
// Start from the current settings // Start from the current settings
new_termios = old_stdin; new_termios = old_stdin;
// Make the terminal like an SSH or telnet client // Make the terminal like an SSH or telnet client
new_termios.c_iflag |= IGNPAR; new_termios.c_iflag |= IGNPAR;
new_termios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); new_termios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
new_termios.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); new_termios.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
new_termios.c_oflag &= ~OPOST; new_termios.c_oflag &= ~OPOST;
new_termios.c_cc[VMIN] = 1; new_termios.c_cc[VMIN] = 1;
new_termios.c_cc[VTIME] = 0; new_termios.c_cc[VTIME] = 0;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_termios) < 0) { if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_termios) < 0) {
return -1; return -1;
} }
stdin_is_raw = 1; stdin_is_raw = 1;
return 0; return 0;
} }
/** /**
@@ -165,15 +165,15 @@ int set_stdin_raw(void) {
* on success, 0 * on success, 0
*/ */
int restore_stdin(void) { int restore_stdin(void) {
if (!stdin_is_raw) return 0; if (!stdin_is_raw) return 0;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_stdin) < 0) { if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_stdin) < 0) {
return -1; return -1;
} }
stdin_is_raw = 0; stdin_is_raw = 0;
return 0; return 0;
} }
// Flag indicating whether the sigwinch watcher should terminate. // Flag indicating whether the sigwinch watcher should terminate.
@@ -184,30 +184,30 @@ static volatile bool close_sigwinch_watcher = false;
* the terminal size. * the terminal size.
*/ */
static void *watch_sigwinch(void *data) { static void *watch_sigwinch(void *data) {
sigset_t winch; sigset_t winch;
int *fds = (int *)data; int *fds = (int *)data;
int sig; int sig;
sigemptyset(&winch); sigemptyset(&winch);
sigaddset(&winch, SIGWINCH); sigaddset(&winch, SIGWINCH);
pthread_sigmask(SIG_UNBLOCK, &winch, nullptr); pthread_sigmask(SIG_UNBLOCK, &winch, nullptr);
do { do {
if (close_sigwinch_watcher) if (close_sigwinch_watcher)
break; break;
// Get the new terminal size // Get the new terminal size
struct winsize w; struct winsize w;
if (ioctl(fds[0], TIOCGWINSZ, &w) == -1) if (ioctl(fds[0], TIOCGWINSZ, &w) == -1)
continue; continue;
// Set the new terminal size // Set the new terminal size
ioctl(fds[1], TIOCSWINSZ, &w); ioctl(fds[1], TIOCSWINSZ, &w);
} while (sigwait(&winch, &sig) == 0); } while (sigwait(&winch, &sig) == 0);
delete[] fds; delete[] fds;
return nullptr; return nullptr;
} }
/** /**
@@ -233,30 +233,30 @@ static void *watch_sigwinch(void *data) {
* on success, 0 * on success, 0
*/ */
int watch_sigwinch_async(int master, int slave) { int watch_sigwinch_async(int master, int slave) {
pthread_t watcher; pthread_t watcher;
int *fds = new int[2]; int *fds = new int[2];
// Block SIGWINCH so sigwait can later receive it // Block SIGWINCH so sigwait can later receive it
sigset_t winch; sigset_t winch;
sigemptyset(&winch); sigemptyset(&winch);
sigaddset(&winch, SIGWINCH); sigaddset(&winch, SIGWINCH);
if (pthread_sigmask(SIG_BLOCK, &winch, nullptr) == -1) { if (pthread_sigmask(SIG_BLOCK, &winch, nullptr) == -1) {
delete[] fds; delete[] fds;
return -1; return -1;
} }
// Initialize some variables, then start the thread // Initialize some variables, then start the thread
close_sigwinch_watcher = 0; close_sigwinch_watcher = 0;
fds[0] = master; fds[0] = master;
fds[1] = slave; fds[1] = slave;
int ret = pthread_create(&watcher, nullptr, &watch_sigwinch, fds); int ret = pthread_create(&watcher, nullptr, &watch_sigwinch, fds);
if (ret != 0) { if (ret != 0) {
delete[] fds; delete[] fds;
errno = ret; errno = ret;
return -1; return -1;
} }
return 0; return 0;
} }
/** /**
@@ -266,11 +266,11 @@ int watch_sigwinch_async(int master, int slave) {
* in a seperate thread * in a seperate thread
*/ */
void pump_stdin_async(int outfd) { void pump_stdin_async(int outfd) {
// Put stdin into raw mode // Put stdin into raw mode
set_stdin_raw(); set_stdin_raw();
// Pump data from stdin to the PTY // Pump data from stdin to the PTY
pump_async(STDIN_FILENO, outfd); pump_async(STDIN_FILENO, outfd);
} }
/** /**
@@ -282,11 +282,11 @@ void pump_stdin_async(int outfd) {
* Before returning, restores stdin settings. * Before returning, restores stdin settings.
*/ */
void pump_stdout_blocking(int infd) { void pump_stdout_blocking(int infd) {
// Pump data from stdout to PTY // Pump data from stdout to PTY
pump(infd, STDOUT_FILENO, false /* Don't close output when done */); pump(infd, STDOUT_FILENO, false /* Don't close output when done */);
// Cleanup // Cleanup
restore_stdin(); restore_stdin();
close_sigwinch_watcher = true; close_sigwinch_watcher = true;
raise(SIGWINCH); raise(SIGWINCH);
} }

View File

@@ -31,200 +31,200 @@
int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 }; int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 };
static void usage(int status) { static void usage(int status) {
FILE *stream = (status == EXIT_SUCCESS) ? stdout : stderr; FILE *stream = (status == EXIT_SUCCESS) ? stdout : stderr;
fprintf(stream, fprintf(stream,
"MagiskSU\n\n" "MagiskSU\n\n"
"Usage: su [options] [-] [user [argument...]]\n\n" "Usage: su [options] [-] [user [argument...]]\n\n"
"Options:\n" "Options:\n"
" -c, --command COMMAND pass COMMAND to the invoked shell\n" " -c, --command COMMAND pass COMMAND to the invoked shell\n"
" -h, --help display this help message and exit\n" " -h, --help display this help message and exit\n"
" -, -l, --login pretend the shell to be a login shell\n" " -, -l, --login pretend the shell to be a login shell\n"
" -m, -p,\n" " -m, -p,\n"
" --preserve-environment preserve the entire environment\n" " --preserve-environment preserve the entire environment\n"
" -s, --shell SHELL use SHELL instead of the default " DEFAULT_SHELL "\n" " -s, --shell SHELL use SHELL instead of the default " DEFAULT_SHELL "\n"
" -v, --version display version number and exit\n" " -v, --version display version number and exit\n"
" -V display version code and exit\n" " -V display version code and exit\n"
" -mm, -M,\n" " -mm, -M,\n"
" --mount-master force run in the global mount namespace\n"); " --mount-master force run in the global mount namespace\n");
exit(status); exit(status);
} }
static char *concat_commands(int argc, char *argv[]) { static char *concat_commands(int argc, char *argv[]) {
char command[ARG_MAX]; char command[ARG_MAX];
command[0] = '\0'; command[0] = '\0';
for (int i = optind - 1; i < argc; ++i) { for (int i = optind - 1; i < argc; ++i) {
if (command[0]) if (command[0])
sprintf(command, "%s %s", command, argv[i]); sprintf(command, "%s %s", command, argv[i]);
else else
strcpy(command, argv[i]); strcpy(command, argv[i]);
} }
return strdup(command); return strdup(command);
} }
static void sighandler(int sig) { static void sighandler(int sig) {
restore_stdin(); restore_stdin();
// Assume we'll only be called before death // Assume we'll only be called before death
// See note before sigaction() in set_stdin_raw() // See note before sigaction() in set_stdin_raw()
// //
// Now, close all standard I/O to cause the pumps // Now, close all standard I/O to cause the pumps
// to exit so we can continue and retrieve the exit // to exit so we can continue and retrieve the exit
// code // code
close(STDIN_FILENO); close(STDIN_FILENO);
close(STDOUT_FILENO); close(STDOUT_FILENO);
close(STDERR_FILENO); close(STDERR_FILENO);
// Put back all the default handlers // Put back all the default handlers
struct sigaction act; struct sigaction act;
memset(&act, 0, sizeof(act)); memset(&act, 0, sizeof(act));
act.sa_handler = SIG_DFL; act.sa_handler = SIG_DFL;
for (int i = 0; quit_signals[i]; ++i) { for (int i = 0; quit_signals[i]; ++i) {
sigaction(quit_signals[i], &act, nullptr); sigaction(quit_signals[i], &act, nullptr);
} }
} }
static void setup_sighandlers(void (*handler)(int)) { static void setup_sighandlers(void (*handler)(int)) {
struct sigaction act; struct sigaction act;
memset(&act, 0, sizeof(act)); memset(&act, 0, sizeof(act));
act.sa_handler = handler; act.sa_handler = handler;
for (int i = 0; quit_signals[i]; ++i) { for (int i = 0; quit_signals[i]; ++i) {
sigaction(quit_signals[i], &act, nullptr); sigaction(quit_signals[i], &act, nullptr);
} }
} }
int su_client_main(int argc, char *argv[]) { int su_client_main(int argc, char *argv[]) {
int c; int c;
struct option long_opts[] = { struct option long_opts[] = {
{ "command", required_argument, nullptr, 'c' }, { "command", required_argument, nullptr, 'c' },
{ "help", no_argument, nullptr, 'h' }, { "help", no_argument, nullptr, 'h' },
{ "login", no_argument, nullptr, 'l' }, { "login", no_argument, nullptr, 'l' },
{ "preserve-environment", no_argument, nullptr, 'p' }, { "preserve-environment", no_argument, nullptr, 'p' },
{ "shell", required_argument, nullptr, 's' }, { "shell", required_argument, nullptr, 's' },
{ "version", no_argument, nullptr, 'v' }, { "version", no_argument, nullptr, 'v' },
{ "context", required_argument, nullptr, 'z' }, { "context", required_argument, nullptr, 'z' },
{ "mount-master", no_argument, nullptr, 'M' }, { "mount-master", no_argument, nullptr, 'M' },
{ nullptr, 0, nullptr, 0 }, { nullptr, 0, nullptr, 0 },
}; };
su_request su_req; su_request su_req;
for (int i = 0; i < argc; i++) { for (int i = 0; i < argc; i++) {
// Replace -cn with -z, -mm with -M for supporting getopt_long // Replace -cn with -z, -mm with -M for supporting getopt_long
if (strcmp(argv[i], "-cn") == 0) if (strcmp(argv[i], "-cn") == 0)
strcpy(argv[i], "-z"); strcpy(argv[i], "-z");
else if (strcmp(argv[i], "-mm") == 0) else if (strcmp(argv[i], "-mm") == 0)
strcpy(argv[i], "-M"); strcpy(argv[i], "-M");
} }
while ((c = getopt_long(argc, argv, "c:hlmps:Vvuz:M", long_opts, nullptr)) != -1) { while ((c = getopt_long(argc, argv, "c:hlmps:Vvuz:M", long_opts, nullptr)) != -1) {
switch (c) { switch (c) {
case 'c': case 'c':
su_req.command = concat_commands(argc, argv); su_req.command = concat_commands(argc, argv);
optind = argc; optind = argc;
break; break;
case 'h': case 'h':
usage(EXIT_SUCCESS); usage(EXIT_SUCCESS);
break; break;
case 'l': case 'l':
su_req.login = true; su_req.login = true;
break; break;
case 'm': case 'm':
case 'p': case 'p':
su_req.keepenv = true; su_req.keepenv = true;
break; break;
case 's': case 's':
su_req.shell = optarg; su_req.shell = optarg;
break; break;
case 'V': case 'V':
printf("%d\n", MAGISK_VER_CODE); printf("%d\n", MAGISK_VER_CODE);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
case 'v': case 'v':
printf("%s\n", MAGISK_VERSION ":MAGISKSU"); printf("%s\n", MAGISK_VERSION ":MAGISKSU");
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
case 'z': case 'z':
// Do nothing, placed here for legacy support :) // Do nothing, placed here for legacy support :)
break; break;
case 'M': case 'M':
su_req.mount_master = true; su_req.mount_master = true;
break; break;
default: default:
/* Bionic getopt_long doesn't terminate its error output by newline */ /* Bionic getopt_long doesn't terminate its error output by newline */
fprintf(stderr, "\n"); fprintf(stderr, "\n");
usage(2); usage(2);
} }
} }
if (optind < argc && strcmp(argv[optind], "-") == 0) { if (optind < argc && strcmp(argv[optind], "-") == 0) {
su_req.login = true; su_req.login = true;
optind++; optind++;
} }
/* username or uid */ /* username or uid */
if (optind < argc) { if (optind < argc) {
struct passwd *pw; struct passwd *pw;
pw = getpwnam(argv[optind]); pw = getpwnam(argv[optind]);
if (pw) if (pw)
su_req.uid = pw->pw_uid; su_req.uid = pw->pw_uid;
else else
su_req.uid = parse_int(argv[optind]); su_req.uid = parse_int(argv[optind]);
optind++; optind++;
} }
char pts_slave[PATH_MAX]; char pts_slave[PATH_MAX];
int ptmx, fd; int ptmx, fd;
// Connect to client // Connect to client
fd = connect_daemon(); fd = connect_daemon();
// Tell the daemon we are su // Tell the daemon we are su
write_int(fd, SUPERUSER); write_int(fd, SUPERUSER);
// Send su_request // Send su_request
xwrite(fd, &su_req, sizeof(su_req_base)); xwrite(fd, &su_req, sizeof(su_req_base));
write_string(fd, su_req.shell); write_string(fd, su_req.shell);
write_string(fd, su_req.command); write_string(fd, su_req.command);
// Wait for ack from daemon // Wait for ack from daemon
if (read_int(fd)) { if (read_int(fd)) {
// Fast fail // Fast fail
fprintf(stderr, "%s\n", strerror(EACCES)); fprintf(stderr, "%s\n", strerror(EACCES));
return EACCES; return EACCES;
} }
// Determine which one of our streams are attached to a TTY // Determine which one of our streams are attached to a TTY
int atty = 0; int atty = 0;
if (isatty(STDIN_FILENO)) atty |= ATTY_IN; if (isatty(STDIN_FILENO)) atty |= ATTY_IN;
if (isatty(STDOUT_FILENO)) atty |= ATTY_OUT; if (isatty(STDOUT_FILENO)) atty |= ATTY_OUT;
if (isatty(STDERR_FILENO)) atty |= ATTY_ERR; if (isatty(STDERR_FILENO)) atty |= ATTY_ERR;
if (atty) { if (atty) {
// We need a PTY. Get one. // We need a PTY. Get one.
ptmx = pts_open(pts_slave, sizeof(pts_slave)); ptmx = pts_open(pts_slave, sizeof(pts_slave));
} else { } else {
pts_slave[0] = '\0'; pts_slave[0] = '\0';
} }
// Send pts_slave // Send pts_slave
write_string(fd, pts_slave); write_string(fd, pts_slave);
// Send stdin // Send stdin
send_fd(fd, (atty & ATTY_IN) ? -1 : STDIN_FILENO); send_fd(fd, (atty & ATTY_IN) ? -1 : STDIN_FILENO);
// Send stdout // Send stdout
send_fd(fd, (atty & ATTY_OUT) ? -1 : STDOUT_FILENO); send_fd(fd, (atty & ATTY_OUT) ? -1 : STDOUT_FILENO);
// Send stderr // Send stderr
send_fd(fd, (atty & ATTY_ERR) ? -1 : STDERR_FILENO); send_fd(fd, (atty & ATTY_ERR) ? -1 : STDERR_FILENO);
if (atty) { if (atty) {
setup_sighandlers(sighandler); setup_sighandlers(sighandler);
watch_sigwinch_async(STDOUT_FILENO, ptmx); watch_sigwinch_async(STDOUT_FILENO, ptmx);
pump_stdin_async(ptmx); pump_stdin_async(ptmx);
pump_stdout_blocking(ptmx); pump_stdout_blocking(ptmx);
} }
// Get the exit code // Get the exit code
int code = read_int(fd); int code = read_int(fd);
close(fd); close(fd);
return code; return code;
} }

View File

@@ -16,54 +16,54 @@
class su_info { class su_info {
public: public:
/* Unique key */ /* Unique key */
const int uid; const int uid;
/* These should be guarded with internal lock */ /* These should be guarded with internal lock */
db_settings cfg; db_settings cfg;
db_strings str; db_strings str;
su_access access; su_access access;
struct stat mgr_st; struct stat mgr_st;
/* This should be guarded with global cache lock */ /* This should be guarded with global cache lock */
long timestamp; long timestamp;
su_info(unsigned uid = 0); su_info(unsigned uid = 0);
~su_info(); ~su_info();
mutex_guard lock(); mutex_guard lock();
bool is_fresh(); bool is_fresh();
void refresh(); void refresh();
private: private:
pthread_mutex_t _lock; /* Internal lock */ pthread_mutex_t _lock; /* Internal lock */
}; };
struct su_req_base { struct su_req_base {
int uid = UID_ROOT; int uid = UID_ROOT;
bool login = false; bool login = false;
bool keepenv = false; bool keepenv = false;
bool mount_master = false; bool mount_master = false;
} __attribute__((packed)); } __attribute__((packed));
struct su_request : public su_req_base { struct su_request : public su_req_base {
const char *shell = DEFAULT_SHELL; const char *shell = DEFAULT_SHELL;
const char *command = ""; const char *command = "";
su_request(bool dyn = false) : dyn(dyn) {} su_request(bool dyn = false) : dyn(dyn) {}
~su_request() { ~su_request() {
if (dyn) { if (dyn) {
free(const_cast<char*>(shell)); free(const_cast<char*>(shell));
free(const_cast<char*>(command)); free(const_cast<char*>(command));
} }
} }
private: private:
bool dyn; bool dyn;
} __attribute__((packed)); } __attribute__((packed));
struct su_context { struct su_context {
std::shared_ptr<su_info> info; std::shared_ptr<su_info> info;
su_request req; su_request req;
int pid; int pid;
}; };
void app_log(const su_context &ctx); void app_log(const su_context &ctx);

View File

@@ -25,319 +25,319 @@ static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
static shared_ptr<su_info> cached; static shared_ptr<su_info> cached;
su_info::su_info(unsigned uid) : su_info::su_info(unsigned uid) :
uid(uid), access(DEFAULT_SU_ACCESS), mgr_st({}), uid(uid), access(DEFAULT_SU_ACCESS), mgr_st({}),
timestamp(0), _lock(PTHREAD_MUTEX_INITIALIZER) {} timestamp(0), _lock(PTHREAD_MUTEX_INITIALIZER) {}
su_info::~su_info() { su_info::~su_info() {
pthread_mutex_destroy(&_lock); pthread_mutex_destroy(&_lock);
} }
mutex_guard su_info::lock() { mutex_guard su_info::lock() {
return mutex_guard(_lock); return mutex_guard(_lock);
} }
bool su_info::is_fresh() { bool su_info::is_fresh() {
timespec ts; timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);
long current = ts.tv_sec * 1000L + ts.tv_nsec / 1000000L; long current = ts.tv_sec * 1000L + ts.tv_nsec / 1000000L;
return current - timestamp < 3000; /* 3 seconds */ return current - timestamp < 3000; /* 3 seconds */
} }
void su_info::refresh() { void su_info::refresh() {
timespec ts; timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);
timestamp = ts.tv_sec * 1000L + ts.tv_nsec / 1000000L; timestamp = ts.tv_sec * 1000L + ts.tv_nsec / 1000000L;
} }
static void database_check(const shared_ptr<su_info> &info) { static void database_check(const shared_ptr<su_info> &info) {
int uid = info->uid; int uid = info->uid;
get_db_settings(info->cfg); get_db_settings(info->cfg);
get_db_strings(info->str); get_db_strings(info->str);
// Check multiuser settings // Check multiuser settings
switch (info->cfg[SU_MULTIUSER_MODE]) { switch (info->cfg[SU_MULTIUSER_MODE]) {
case MULTIUSER_MODE_OWNER_ONLY: case MULTIUSER_MODE_OWNER_ONLY:
if (info->uid / 100000) { if (info->uid / 100000) {
uid = -1; uid = -1;
info->access = NO_SU_ACCESS; info->access = NO_SU_ACCESS;
} }
break; break;
case MULTIUSER_MODE_OWNER_MANAGED: case MULTIUSER_MODE_OWNER_MANAGED:
uid = info->uid % 100000; uid = info->uid % 100000;
break; break;
case MULTIUSER_MODE_USER: case MULTIUSER_MODE_USER:
default: default:
break; break;
} }
if (uid > 0) if (uid > 0)
get_uid_policy(info->access, uid); get_uid_policy(info->access, uid);
// We need to check our manager // We need to check our manager
if (info->access.log || info->access.notify) if (info->access.log || info->access.notify)
validate_manager(info->str[SU_MANAGER], uid / 100000, &info->mgr_st); validate_manager(info->str[SU_MANAGER], uid / 100000, &info->mgr_st);
} }
static shared_ptr<su_info> get_su_info(unsigned uid) { static shared_ptr<su_info> get_su_info(unsigned uid) {
LOGD("su: request from uid=[%d]\n", uid); LOGD("su: request from uid=[%d]\n", uid);
shared_ptr<su_info> info; shared_ptr<su_info> info;
{ {
mutex_guard lock(cache_lock); mutex_guard lock(cache_lock);
if (!cached || cached->uid != uid || !cached->is_fresh()) if (!cached || cached->uid != uid || !cached->is_fresh())
cached = make_shared<su_info>(uid); cached = make_shared<su_info>(uid);
cached->refresh(); cached->refresh();
info = cached; info = cached;
} }
mutex_guard lock = info->lock(); mutex_guard lock = info->lock();
if (info->access.policy == QUERY) { if (info->access.policy == QUERY) {
// Not cached, get data from database // Not cached, get data from database
database_check(info); database_check(info);
// If it's root or the manager, allow it silently // If it's root or the manager, allow it silently
if (info->uid == UID_ROOT || (info->uid % 100000) == (info->mgr_st.st_uid % 100000)) { if (info->uid == UID_ROOT || (info->uid % 100000) == (info->mgr_st.st_uid % 100000)) {
info->access = SILENT_SU_ACCESS; info->access = SILENT_SU_ACCESS;
return info; return info;
} }
// Check su access settings // Check su access settings
switch (info->cfg[ROOT_ACCESS]) { switch (info->cfg[ROOT_ACCESS]) {
case ROOT_ACCESS_DISABLED: case ROOT_ACCESS_DISABLED:
LOGW("Root access is disabled!\n"); LOGW("Root access is disabled!\n");
info->access = NO_SU_ACCESS; info->access = NO_SU_ACCESS;
break; break;
case ROOT_ACCESS_ADB_ONLY: case ROOT_ACCESS_ADB_ONLY:
if (info->uid != UID_SHELL) { if (info->uid != UID_SHELL) {
LOGW("Root access limited to ADB only!\n"); LOGW("Root access limited to ADB only!\n");
info->access = NO_SU_ACCESS; info->access = NO_SU_ACCESS;
} }
break; break;
case ROOT_ACCESS_APPS_ONLY: case ROOT_ACCESS_APPS_ONLY:
if (info->uid == UID_SHELL) { if (info->uid == UID_SHELL) {
LOGW("Root access is disabled for ADB!\n"); LOGW("Root access is disabled for ADB!\n");
info->access = NO_SU_ACCESS; info->access = NO_SU_ACCESS;
} }
break; break;
case ROOT_ACCESS_APPS_AND_ADB: case ROOT_ACCESS_APPS_AND_ADB:
default: default:
break; break;
} }
if (info->access.policy != QUERY) if (info->access.policy != QUERY)
return info; return info;
// If still not determined, check if manager exists // If still not determined, check if manager exists
if (info->str[SU_MANAGER].empty()) { if (info->str[SU_MANAGER].empty()) {
info->access = NO_SU_ACCESS; info->access = NO_SU_ACCESS;
return info; return info;
} }
} else { } else {
return info; return info;
} }
// If still not determined, ask manager // If still not determined, ask manager
int fd = app_request(info); int fd = app_request(info);
if (fd < 0) { if (fd < 0) {
info->access.policy = DENY; info->access.policy = DENY;
} else { } else {
int ret = read_int_be(fd); int ret = read_int_be(fd);
info->access.policy = ret < 0 ? DENY : static_cast<policy_t>(ret); info->access.policy = ret < 0 ? DENY : static_cast<policy_t>(ret);
close(fd); close(fd);
} }
return info; return info;
} }
// Set effective uid back to root, otherwise setres[ug]id will fail if uid isn't root // Set effective uid back to root, otherwise setres[ug]id will fail if uid isn't root
static void set_identity(unsigned uid) { static void set_identity(unsigned uid) {
if (seteuid(0)) { if (seteuid(0)) {
PLOGE("seteuid (root)"); PLOGE("seteuid (root)");
} }
if (setresgid(uid, uid, uid)) { if (setresgid(uid, uid, uid)) {
PLOGE("setresgid (%u)", uid); PLOGE("setresgid (%u)", uid);
} }
if (setresuid(uid, uid, uid)) { if (setresuid(uid, uid, uid)) {
PLOGE("setresuid (%u)", uid); PLOGE("setresuid (%u)", uid);
} }
} }
void su_daemon_handler(int client, struct ucred *credential) { void su_daemon_handler(int client, struct ucred *credential) {
LOGD("su: request from pid=[%d], client=[%d]\n", credential->pid, client); LOGD("su: request from pid=[%d], client=[%d]\n", credential->pid, client);
su_context ctx = { su_context ctx = {
.info = get_su_info(credential->uid), .info = get_su_info(credential->uid),
.req = su_request(true), .req = su_request(true),
.pid = credential->pid .pid = credential->pid
}; };
// Read su_request // Read su_request
xxread(client, &ctx.req, sizeof(su_req_base)); xxread(client, &ctx.req, sizeof(su_req_base));
ctx.req.shell = read_string(client); ctx.req.shell = read_string(client);
ctx.req.command = read_string(client); ctx.req.command = read_string(client);
if (ctx.info->access.log) if (ctx.info->access.log)
app_log(ctx); app_log(ctx);
else if (ctx.info->access.notify) else if (ctx.info->access.notify)
app_notify(ctx); app_notify(ctx);
// Fail fast // Fail fast
if (ctx.info->access.policy == DENY) { if (ctx.info->access.policy == DENY) {
LOGW("su: request rejected (%u)\n", ctx.info->uid); LOGW("su: request rejected (%u)\n", ctx.info->uid);
ctx.info.reset(); ctx.info.reset();
write_int(client, DENY); write_int(client, DENY);
close(client); close(client);
return; return;
} }
// Fork a child root process // Fork a child root process
// //
// The child process will need to setsid, open a pseudo-terminal // The child process will need to setsid, open a pseudo-terminal
// if needed, and eventually exec shell. // if needed, and eventually exec shell.
// The parent process will wait for the result and // The parent process will wait for the result and
// send the return code back to our client. // send the return code back to our client.
if (int child = xfork(); child) { if (int child = xfork(); child) {
ctx.info.reset(); ctx.info.reset();
// Wait result // Wait result
LOGD("su: waiting child pid=[%d]\n", child); LOGD("su: waiting child pid=[%d]\n", child);
int status, code; int status, code;
if (waitpid(child, &status, 0) > 0) if (waitpid(child, &status, 0) > 0)
code = WEXITSTATUS(status); code = WEXITSTATUS(status);
else else
code = -1; code = -1;
LOGD("su: return code=[%d]\n", code); LOGD("su: return code=[%d]\n", code);
write(client, &code, sizeof(code)); write(client, &code, sizeof(code));
close(client); close(client);
return; return;
} }
LOGD("su: fork handler\n"); LOGD("su: fork handler\n");
// Abort upon any error occurred // Abort upon any error occurred
log_cb.ex = exit; log_cb.ex = exit;
// ack // ack
write_int(client, 0); write_int(client, 0);
// Become session leader // Become session leader
xsetsid(); xsetsid();
// Get pts_slave // Get pts_slave
char *pts_slave = read_string(client); char *pts_slave = read_string(client);
// The FDs for each of the streams // The FDs for each of the streams
int infd = recv_fd(client); int infd = recv_fd(client);
int outfd = recv_fd(client); int outfd = recv_fd(client);
int errfd = recv_fd(client); int errfd = recv_fd(client);
if (pts_slave[0]) { if (pts_slave[0]) {
LOGD("su: pts_slave=[%s]\n", pts_slave); LOGD("su: pts_slave=[%s]\n", pts_slave);
// Check pts_slave file is owned by daemon_from_uid // Check pts_slave file is owned by daemon_from_uid
struct stat st; struct stat st;
xstat(pts_slave, &st); xstat(pts_slave, &st);
// If caller is not root, ensure the owner of pts_slave is the caller // If caller is not root, ensure the owner of pts_slave is the caller
if(st.st_uid != ctx.info->uid && ctx.info->uid != 0) if(st.st_uid != ctx.info->uid && ctx.info->uid != 0)
LOGE("su: Wrong permission of pts_slave\n"); LOGE("su: Wrong permission of pts_slave\n");
// Opening the TTY has to occur after the // Opening the TTY has to occur after the
// fork() and setsid() so that it becomes // fork() and setsid() so that it becomes
// our controlling TTY and not the daemon's // our controlling TTY and not the daemon's
int ptsfd = xopen(pts_slave, O_RDWR); int ptsfd = xopen(pts_slave, O_RDWR);
if (infd < 0) if (infd < 0)
infd = ptsfd; infd = ptsfd;
if (outfd < 0) if (outfd < 0)
outfd = ptsfd; outfd = ptsfd;
if (errfd < 0) if (errfd < 0)
errfd = ptsfd; errfd = ptsfd;
} }
free(pts_slave); free(pts_slave);
// Swap out stdin, stdout, stderr // Swap out stdin, stdout, stderr
xdup2(infd, STDIN_FILENO); xdup2(infd, STDIN_FILENO);
xdup2(outfd, STDOUT_FILENO); xdup2(outfd, STDOUT_FILENO);
xdup2(errfd, STDERR_FILENO); xdup2(errfd, STDERR_FILENO);
// Unleash all streams from SELinux hell // Unleash all streams from SELinux hell
setfilecon("/proc/self/fd/0", "u:object_r:" SEPOL_FILE_TYPE ":s0"); setfilecon("/proc/self/fd/0", "u:object_r:" SEPOL_FILE_TYPE ":s0");
setfilecon("/proc/self/fd/1", "u:object_r:" SEPOL_FILE_TYPE ":s0"); setfilecon("/proc/self/fd/1", "u:object_r:" SEPOL_FILE_TYPE ":s0");
setfilecon("/proc/self/fd/2", "u:object_r:" SEPOL_FILE_TYPE ":s0"); setfilecon("/proc/self/fd/2", "u:object_r:" SEPOL_FILE_TYPE ":s0");
close(infd); close(infd);
close(outfd); close(outfd);
close(errfd); close(errfd);
close(client); close(client);
// Handle namespaces // Handle namespaces
if (ctx.req.mount_master) if (ctx.req.mount_master)
ctx.info->cfg[SU_MNT_NS] = NAMESPACE_MODE_GLOBAL; ctx.info->cfg[SU_MNT_NS] = NAMESPACE_MODE_GLOBAL;
switch (ctx.info->cfg[SU_MNT_NS]) { switch (ctx.info->cfg[SU_MNT_NS]) {
case NAMESPACE_MODE_GLOBAL: case NAMESPACE_MODE_GLOBAL:
LOGD("su: use global namespace\n"); LOGD("su: use global namespace\n");
break; break;
case NAMESPACE_MODE_REQUESTER: case NAMESPACE_MODE_REQUESTER:
LOGD("su: use namespace of pid=[%d]\n", ctx.pid); LOGD("su: use namespace of pid=[%d]\n", ctx.pid);
if (switch_mnt_ns(ctx.pid)) if (switch_mnt_ns(ctx.pid))
LOGD("su: setns failed, fallback to global\n"); LOGD("su: setns failed, fallback to global\n");
break; break;
case NAMESPACE_MODE_ISOLATE: case NAMESPACE_MODE_ISOLATE:
LOGD("su: use new isolated namespace\n"); LOGD("su: use new isolated namespace\n");
switch_mnt_ns(ctx.pid); switch_mnt_ns(ctx.pid);
xunshare(CLONE_NEWNS); xunshare(CLONE_NEWNS);
xmount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr); xmount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr);
break; break;
} }
const char *argv[] = { nullptr, nullptr, nullptr, nullptr }; const char *argv[] = { nullptr, nullptr, nullptr, nullptr };
argv[0] = ctx.req.login ? "-" : ctx.req.shell; argv[0] = ctx.req.login ? "-" : ctx.req.shell;
if (ctx.req.command[0]) { if (ctx.req.command[0]) {
argv[1] = "-c"; argv[1] = "-c";
argv[2] = ctx.req.command; argv[2] = ctx.req.command;
} }
// Setup environment // Setup environment
umask(022); umask(022);
char path[32]; char path[32];
snprintf(path, sizeof(path), "/proc/%d/cwd", ctx.pid); snprintf(path, sizeof(path), "/proc/%d/cwd", ctx.pid);
chdir(path); chdir(path);
snprintf(path, sizeof(path), "/proc/%d/environ", ctx.pid); snprintf(path, sizeof(path), "/proc/%d/environ", ctx.pid);
char buf[4096] = { 0 }; char buf[4096] = { 0 };
int fd = xopen(path, O_RDONLY); int fd = xopen(path, O_RDONLY);
read(fd, buf, sizeof(buf)); read(fd, buf, sizeof(buf));
close(fd); close(fd);
clearenv(); clearenv();
for (size_t pos = 0; buf[pos];) { for (size_t pos = 0; buf[pos];) {
putenv(buf + pos); putenv(buf + pos);
pos += strlen(buf + pos) + 1; pos += strlen(buf + pos) + 1;
} }
if (!ctx.req.keepenv) { if (!ctx.req.keepenv) {
struct passwd *pw; struct passwd *pw;
pw = getpwuid(ctx.req.uid); pw = getpwuid(ctx.req.uid);
if (pw) { if (pw) {
setenv("HOME", pw->pw_dir, 1); setenv("HOME", pw->pw_dir, 1);
setenv("USER", pw->pw_name, 1); setenv("USER", pw->pw_name, 1);
setenv("LOGNAME", pw->pw_name, 1); setenv("LOGNAME", pw->pw_name, 1);
setenv("SHELL", ctx.req.shell, 1); setenv("SHELL", ctx.req.shell, 1);
} }
} }
// Unblock all signals // Unblock all signals
sigset_t block_set; sigset_t block_set;
sigemptyset(&block_set); sigemptyset(&block_set);
sigprocmask(SIG_SETMASK, &block_set, nullptr); sigprocmask(SIG_SETMASK, &block_set, nullptr);
set_identity(ctx.req.uid); set_identity(ctx.req.uid);
execvp(ctx.req.shell, (char **) argv); execvp(ctx.req.shell, (char **) argv);
fprintf(stderr, "Cannot execute %s: %s\n", ctx.req.shell, strerror(errno)); fprintf(stderr, "Cannot execute %s: %s\n", ctx.req.shell, strerror(errno));
PLOGE("exec"); PLOGE("exec");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }

View File

@@ -5,13 +5,13 @@ LOCAL_MODULE:= libutils
LOCAL_C_INCLUDES := jni/include $(LOCAL_PATH)/include LOCAL_C_INCLUDES := jni/include $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
missing.cpp \ missing.cpp \
new.cpp \ new.cpp \
files.cpp \ files.cpp \
misc.cpp \ misc.cpp \
selinux.cpp \ selinux.cpp \
logging.cpp \ logging.cpp \
xwrap.cpp \ xwrap.cpp \
stream.cpp stream.cpp
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)

View File

@@ -10,264 +10,264 @@
using namespace std; using namespace std;
struct cpio_newc_header { struct cpio_newc_header {
char magic[6]; char magic[6];
char ino[8]; char ino[8];
char mode[8]; char mode[8];
char uid[8]; char uid[8];
char gid[8]; char gid[8];
char nlink[8]; char nlink[8];
char mtime[8]; char mtime[8];
char filesize[8]; char filesize[8];
char devmajor[8]; char devmajor[8];
char devminor[8]; char devminor[8];
char rdevmajor[8]; char rdevmajor[8];
char rdevminor[8]; char rdevminor[8];
char namesize[8]; char namesize[8];
char check[8]; char check[8];
} __attribute__((packed)); } __attribute__((packed));
static uint32_t x8u(const char *hex) { static uint32_t x8u(const char *hex) {
uint32_t val, inpos = 8, outpos; uint32_t val, inpos = 8, outpos;
char pattern[6]; char pattern[6];
while (*hex == '0') { while (*hex == '0') {
hex++; hex++;
if (!--inpos) return 0; if (!--inpos) return 0;
} }
// Because scanf gratuitously treats %*X differently than printf does. // Because scanf gratuitously treats %*X differently than printf does.
sprintf(pattern, "%%%dx%%n", inpos); sprintf(pattern, "%%%dx%%n", inpos);
sscanf(hex, pattern, &val, &outpos); sscanf(hex, pattern, &val, &outpos);
if (inpos != outpos) if (inpos != outpos)
LOGE("bad cpio header\n"); LOGE("bad cpio header\n");
return val; return val;
} }
cpio_entry_base::cpio_entry_base(const cpio_newc_header *h) cpio_entry_base::cpio_entry_base(const cpio_newc_header *h)
: mode(x8u(h->mode)), uid(x8u(h->uid)), gid(x8u(h->gid)), filesize(x8u(h->filesize)) {}; : mode(x8u(h->mode)), uid(x8u(h->uid)), gid(x8u(h->gid)), filesize(x8u(h->filesize)) {};
void cpio::dump(const char *file) { void cpio::dump(const char *file) {
fprintf(stderr, "Dump cpio: [%s]\n", file); fprintf(stderr, "Dump cpio: [%s]\n", file);
dump(xfopen(file, "we")); dump(xfopen(file, "we"));
} }
void cpio::rm(entry_map::iterator &it) { void cpio::rm(entry_map::iterator &it) {
fprintf(stderr, "Remove [%s]\n", it->first.data()); fprintf(stderr, "Remove [%s]\n", it->first.data());
entries.erase(it); entries.erase(it);
} }
void cpio::rm(const char *name, bool r) { void cpio::rm(const char *name, bool r) {
size_t len = strlen(name); size_t len = strlen(name);
for (auto it = entries.begin(); it != entries.end();) { for (auto it = entries.begin(); it != entries.end();) {
if (it->first.compare(0, len, name) == 0 && if (it->first.compare(0, len, name) == 0 &&
((r && it->first[len] == '/') || it->first[len] == '\0')) { ((r && it->first[len] == '/') || it->first[len] == '\0')) {
auto tmp = it; auto tmp = it;
++it; ++it;
rm(tmp); rm(tmp);
if (!r) return; if (!r) return;
} else { } else {
++it; ++it;
} }
} }
} }
static void extract_entry(const entry_map::value_type &e, const char *file) { static void extract_entry(const entry_map::value_type &e, const char *file) {
fprintf(stderr, "Extract [%s] to [%s]\n", e.first.data(), file); fprintf(stderr, "Extract [%s] to [%s]\n", e.first.data(), file);
unlink(file); unlink(file);
rmdir(file); rmdir(file);
if (S_ISDIR(e.second->mode)) { if (S_ISDIR(e.second->mode)) {
mkdir(file, e.second->mode & 0777); mkdir(file, e.second->mode & 0777);
} else if (S_ISREG(e.second->mode)) { } else if (S_ISREG(e.second->mode)) {
int fd = creat(file, e.second->mode & 0777); int fd = creat(file, e.second->mode & 0777);
xwrite(fd, e.second->data, e.second->filesize); xwrite(fd, e.second->data, e.second->filesize);
fchown(fd, e.second->uid, e.second->gid); fchown(fd, e.second->uid, e.second->gid);
close(fd); close(fd);
} else if (S_ISLNK(e.second->mode)) { } else if (S_ISLNK(e.second->mode)) {
auto target = strndup((char *) e.second->data, e.second->filesize); auto target = strndup((char *) e.second->data, e.second->filesize);
symlink(target, file); symlink(target, file);
free(target); free(target);
} }
} }
void cpio::extract() { void cpio::extract() {
for (auto &e : entries) for (auto &e : entries)
extract_entry(e, e.first.data()); extract_entry(e, e.first.data());
} }
bool cpio::extract(const char *name, const char *file) { bool cpio::extract(const char *name, const char *file) {
auto it = entries.find(name); auto it = entries.find(name);
if (it != entries.end()) { if (it != entries.end()) {
extract_entry(*it, file); extract_entry(*it, file);
return true; return true;
} }
fprintf(stderr, "Cannot find the file entry [%s]\n", name); fprintf(stderr, "Cannot find the file entry [%s]\n", name);
return false; return false;
} }
bool cpio::exists(const char *name) { bool cpio::exists(const char *name) {
return entries.count(name) != 0; return entries.count(name) != 0;
} }
#define do_out(buf, len) pos += fwrite(buf, 1, len, out); #define do_out(buf, len) pos += fwrite(buf, 1, len, out);
#define out_align() do_out(zeros, align_off(pos, 4)) #define out_align() do_out(zeros, align_off(pos, 4))
void cpio::dump(FILE *out) { void cpio::dump(FILE *out) {
size_t pos = 0; size_t pos = 0;
unsigned inode = 300000; unsigned inode = 300000;
char header[111]; char header[111];
char zeros[4] = {0}; char zeros[4] = {0};
for (auto &e : entries) { for (auto &e : entries) {
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x", sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
inode++, // e->ino inode++, // e->ino
e.second->mode, e.second->mode,
e.second->uid, e.second->uid,
e.second->gid, e.second->gid,
1, // e->nlink 1, // e->nlink
0, // e->mtime 0, // e->mtime
e.second->filesize, e.second->filesize,
0, // e->devmajor 0, // e->devmajor
0, // e->devminor 0, // e->devminor
0, // e->rdevmajor 0, // e->rdevmajor
0, // e->rdevminor 0, // e->rdevminor
(uint32_t) e.first.size() + 1, (uint32_t) e.first.size() + 1,
0 // e->check 0 // e->check
); );
do_out(header, 110); do_out(header, 110);
do_out(e.first.data(), e.first.size() + 1); do_out(e.first.data(), e.first.size() + 1);
out_align(); out_align();
if (e.second->filesize) { if (e.second->filesize) {
do_out(e.second->data, e.second->filesize); do_out(e.second->data, e.second->filesize);
out_align(); out_align();
} }
} }
// Write trailer // Write trailer
sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x", sprintf(header, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x",
inode++, 0755, 0, 0, 1, 0, 0, 0, 0, 0, 0, 11, 0); inode++, 0755, 0, 0, 1, 0, 0, 0, 0, 0, 0, 11, 0);
do_out(header, 110); do_out(header, 110);
do_out("TRAILER!!!\0", 11); do_out("TRAILER!!!\0", 11);
out_align(); out_align();
fclose(out); fclose(out);
} }
cpio_rw::cpio_rw(const char *file) { cpio_rw::cpio_rw(const char *file) {
load_cpio(file); load_cpio(file);
} }
void cpio_rw::load_cpio(const char *file) { void cpio_rw::load_cpio(const char *file) {
char *buf; char *buf;
size_t sz; size_t sz;
mmap_ro(file, buf, sz); mmap_ro(file, buf, sz);
fprintf(stderr, "Loading cpio: [%s]\n", file); fprintf(stderr, "Loading cpio: [%s]\n", file);
load_cpio(buf, sz); load_cpio(buf, sz);
munmap(buf, sz); munmap(buf, sz);
} }
void cpio_rw::insert(cpio_entry *e) { void cpio_rw::insert(cpio_entry *e) {
auto ex = entries.extract(e->filename); auto ex = entries.extract(e->filename);
if (!ex) { if (!ex) {
entries[e->filename].reset(e); entries[e->filename].reset(e);
} else { } else {
ex.key() = e->filename; ex.key() = e->filename;
ex.mapped().reset(e); ex.mapped().reset(e);
entries.insert(std::move(ex)); entries.insert(std::move(ex));
} }
} }
void cpio_rw::add(mode_t mode, const char *name, const char *file) { void cpio_rw::add(mode_t mode, const char *name, const char *file) {
void *buf; void *buf;
size_t sz; size_t sz;
mmap_ro(file, buf, sz); mmap_ro(file, buf, sz);
auto e = new cpio_entry(name, S_IFREG | mode); auto e = new cpio_entry(name, S_IFREG | mode);
e->filesize = sz; e->filesize = sz;
e->data = xmalloc(sz); e->data = xmalloc(sz);
memcpy(e->data, buf, sz); memcpy(e->data, buf, sz);
munmap(buf, sz); munmap(buf, sz);
insert(e); insert(e);
fprintf(stderr, "Add entry [%s] (%04o)\n", name, mode); fprintf(stderr, "Add entry [%s] (%04o)\n", name, mode);
} }
void cpio_rw::mkdir(mode_t mode, const char *name) { void cpio_rw::mkdir(mode_t mode, const char *name) {
insert(new cpio_entry(name, S_IFDIR | mode)); insert(new cpio_entry(name, S_IFDIR | mode));
fprintf(stderr, "Create directory [%s] (%04o)\n", name, mode); fprintf(stderr, "Create directory [%s] (%04o)\n", name, mode);
} }
void cpio_rw::ln(const char *target, const char *name) { void cpio_rw::ln(const char *target, const char *name) {
auto e = new cpio_entry(name, S_IFLNK); auto e = new cpio_entry(name, S_IFLNK);
e->filesize = strlen(target); e->filesize = strlen(target);
e->data = strdup(target); e->data = strdup(target);
insert(e); insert(e);
fprintf(stderr, "Create symlink [%s] -> [%s]\n", name, target); fprintf(stderr, "Create symlink [%s] -> [%s]\n", name, target);
} }
void cpio_rw::mv(entry_map::iterator &it, const char *to) { void cpio_rw::mv(entry_map::iterator &it, const char *to) {
fprintf(stderr, "Move [%s] -> [%s]\n", it->first.data(), to); fprintf(stderr, "Move [%s] -> [%s]\n", it->first.data(), to);
auto ex = entries.extract(it); auto ex = entries.extract(it);
auto &name = static_cast<cpio_entry*>(ex.mapped().get())->filename; auto &name = static_cast<cpio_entry*>(ex.mapped().get())->filename;
name = to; name = to;
ex.key() = name; ex.key() = name;
entries.erase(name); entries.erase(name);
entries.insert(std::move(ex)); entries.insert(std::move(ex));
} }
bool cpio_rw::mv(const char *from, const char *to) { bool cpio_rw::mv(const char *from, const char *to) {
auto it = entries.find(from); auto it = entries.find(from);
if (it != entries.end()) { if (it != entries.end()) {
mv(it, to); mv(it, to);
return true; return true;
} }
fprintf(stderr, "Cannot find entry %s\n", from); fprintf(stderr, "Cannot find entry %s\n", from);
return false; return false;
} }
#define pos_align(p) p = do_align(p, 4) #define pos_align(p) p = do_align(p, 4)
void cpio_rw::load_cpio(const char *buf, size_t sz) { void cpio_rw::load_cpio(const char *buf, size_t sz) {
size_t pos = 0; size_t pos = 0;
const cpio_newc_header *header; const cpio_newc_header *header;
unique_ptr<cpio_entry> entry; unique_ptr<cpio_entry> entry;
while (pos < sz) { while (pos < sz) {
header = reinterpret_cast<const cpio_newc_header *>(buf + pos); header = reinterpret_cast<const cpio_newc_header *>(buf + pos);
entry = make_unique<cpio_entry>(header); entry = make_unique<cpio_entry>(header);
pos += sizeof(*header); pos += sizeof(*header);
string_view name_view(buf + pos); string_view name_view(buf + pos);
pos += x8u(header->namesize); pos += x8u(header->namesize);
pos_align(pos); pos_align(pos);
if (name_view == "." || name_view == "..") if (name_view == "." || name_view == "..")
continue; continue;
if (name_view == "TRAILER!!!") if (name_view == "TRAILER!!!")
break; break;
entry->filename = name_view; entry->filename = name_view;
entry->data = xmalloc(entry->filesize); entry->data = xmalloc(entry->filesize);
memcpy(entry->data, buf + pos, entry->filesize); memcpy(entry->data, buf + pos, entry->filesize);
pos += entry->filesize; pos += entry->filesize;
entries[entry->filename] = std::move(entry); entries[entry->filename] = std::move(entry);
pos_align(pos); pos_align(pos);
} }
} }
cpio_mmap::cpio_mmap(const char *file) { cpio_mmap::cpio_mmap(const char *file) {
mmap_ro(file, buf, sz); mmap_ro(file, buf, sz);
fprintf(stderr, "Loading cpio: [%s]\n", file); fprintf(stderr, "Loading cpio: [%s]\n", file);
size_t pos = 0; size_t pos = 0;
cpio_newc_header *header; cpio_newc_header *header;
unique_ptr<cpio_entry_base> entry; unique_ptr<cpio_entry_base> entry;
while (pos < sz) { while (pos < sz) {
header = (cpio_newc_header *)(buf + pos); header = (cpio_newc_header *)(buf + pos);
entry = make_unique<cpio_entry_base>(header); entry = make_unique<cpio_entry_base>(header);
pos += sizeof(*header); pos += sizeof(*header);
string_view name_view(buf + pos); string_view name_view(buf + pos);
pos += x8u(header->namesize); pos += x8u(header->namesize);
pos_align(pos); pos_align(pos);
if (name_view == "." || name_view == "..") if (name_view == "." || name_view == "..")
continue; continue;
if (name_view == "TRAILER!!!") if (name_view == "TRAILER!!!")
break; break;
entry->data = buf + pos; entry->data = buf + pos;
pos += entry->filesize; pos += entry->filesize;
entries[name_view] = std::move(entry); entries[name_view] = std::move(entry);
pos_align(pos); pos_align(pos);
} }
} }
cpio_mmap::~cpio_mmap() { cpio_mmap::~cpio_mmap() {
munmap(buf, sz); munmap(buf, sz);
} }

View File

@@ -13,427 +13,427 @@
using namespace std; using namespace std;
ssize_t fd_path(int fd, char *path, size_t size) { ssize_t fd_path(int fd, char *path, size_t size) {
snprintf(path, size, "/proc/self/fd/%d", fd); snprintf(path, size, "/proc/self/fd/%d", fd);
return xreadlink(path, path, size); return xreadlink(path, path, size);
} }
int fd_pathat(int dirfd, const char *name, char *path, size_t size) { int fd_pathat(int dirfd, const char *name, char *path, size_t size) {
if (fd_path(dirfd, path, size) < 0) if (fd_path(dirfd, path, size) < 0)
return -1; return -1;
auto len = strlen(path); auto len = strlen(path);
path[len] = '/'; path[len] = '/';
strlcpy(path + len + 1, name, size - len - 1); strlcpy(path + len + 1, name, size - len - 1);
return 0; return 0;
} }
int mkdirs(string path, mode_t mode) { int mkdirs(string path, mode_t mode) {
errno = 0; errno = 0;
for (char *p = path.data() + 1; *p; ++p) { for (char *p = path.data() + 1; *p; ++p) {
if (*p == '/') { if (*p == '/') {
*p = '\0'; *p = '\0';
if (mkdir(path.data(), mode) == -1) { if (mkdir(path.data(), mode) == -1) {
if (errno != EEXIST) if (errno != EEXIST)
return -1; return -1;
} }
*p = '/'; *p = '/';
} }
} }
if (mkdir(path.data(), mode) == -1) { if (mkdir(path.data(), mode) == -1) {
if (errno != EEXIST) if (errno != EEXIST)
return -1; return -1;
} }
return 0; return 0;
} }
template <typename Func> template <typename Func>
static void post_order_walk(int dirfd, const Func &fn) { static void post_order_walk(int dirfd, const Func &fn) {
auto dir = xopen_dir(dirfd); auto dir = xopen_dir(dirfd);
if (!dir) return; if (!dir) return;
for (dirent *entry; (entry = xreaddir(dir.get()));) { for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_type == DT_DIR) if (entry->d_type == DT_DIR)
post_order_walk(xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC), fn); post_order_walk(xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC), fn);
fn(dirfd, entry); fn(dirfd, entry);
} }
} }
template <typename Func> template <typename Func>
static void pre_order_walk(int dirfd, const Func &fn) { static void pre_order_walk(int dirfd, const Func &fn) {
auto dir = xopen_dir(dirfd); auto dir = xopen_dir(dirfd);
if (!dir) return; if (!dir) return;
for (dirent *entry; (entry = xreaddir(dir.get()));) { for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (!fn(dirfd, entry)) if (!fn(dirfd, entry))
continue; continue;
if (entry->d_type == DT_DIR) if (entry->d_type == DT_DIR)
pre_order_walk(xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC), fn); pre_order_walk(xopenat(dirfd, entry->d_name, O_RDONLY | O_CLOEXEC), fn);
} }
} }
static void remove_at(int dirfd, struct dirent *entry) { static void remove_at(int dirfd, struct dirent *entry) {
unlinkat(dirfd, entry->d_name, entry->d_type == DT_DIR ? AT_REMOVEDIR : 0); unlinkat(dirfd, entry->d_name, entry->d_type == DT_DIR ? AT_REMOVEDIR : 0);
} }
void rm_rf(const char *path) { void rm_rf(const char *path) {
struct stat st; struct stat st;
if (lstat(path, &st) < 0) if (lstat(path, &st) < 0)
return; return;
if (S_ISDIR(st.st_mode)) if (S_ISDIR(st.st_mode))
frm_rf(xopen(path, O_RDONLY | O_CLOEXEC)); frm_rf(xopen(path, O_RDONLY | O_CLOEXEC));
remove(path); remove(path);
} }
void frm_rf(int dirfd) { void frm_rf(int dirfd) {
post_order_walk(dirfd, remove_at); post_order_walk(dirfd, remove_at);
} }
void mv_path(const char *src, const char *dest) { void mv_path(const char *src, const char *dest) {
file_attr attr; file_attr attr;
getattr(src, &attr); getattr(src, &attr);
if (S_ISDIR(attr.st.st_mode)) { if (S_ISDIR(attr.st.st_mode)) {
if (access(dest, F_OK) != 0) { if (access(dest, F_OK) != 0) {
xmkdirs(dest, 0); xmkdirs(dest, 0);
setattr(dest, &attr); setattr(dest, &attr);
} }
mv_dir(xopen(src, O_RDONLY | O_CLOEXEC), xopen(dest, O_RDONLY | O_CLOEXEC)); mv_dir(xopen(src, O_RDONLY | O_CLOEXEC), xopen(dest, O_RDONLY | O_CLOEXEC));
} else{ } else{
xrename(src, dest); xrename(src, dest);
} }
rmdir(src); rmdir(src);
} }
void mv_dir(int src, int dest) { void mv_dir(int src, int dest) {
auto dir = xopen_dir(src); auto dir = xopen_dir(src);
run_finally f([=]{ close(dest); }); run_finally f([=]{ close(dest); });
for (dirent *entry; (entry = xreaddir(dir.get()));) { for (dirent *entry; (entry = xreaddir(dir.get()));) {
switch (entry->d_type) { switch (entry->d_type) {
case DT_DIR: case DT_DIR:
if (faccessat(dest, entry->d_name, F_OK, 0) == 0) { if (faccessat(dest, entry->d_name, F_OK, 0) == 0) {
// Destination folder exists, needs recursive move // Destination folder exists, needs recursive move
int newsrc = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC); int newsrc = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
int newdest = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC); int newdest = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
mv_dir(newsrc, newdest); mv_dir(newsrc, newdest);
unlinkat(src, entry->d_name, AT_REMOVEDIR); unlinkat(src, entry->d_name, AT_REMOVEDIR);
break; break;
} }
// Else fall through // Else fall through
case DT_LNK: case DT_LNK:
case DT_REG: case DT_REG:
renameat(src, entry->d_name, dest, entry->d_name); renameat(src, entry->d_name, dest, entry->d_name);
break; break;
} }
} }
} }
void cp_afc(const char *src, const char *dest) { void cp_afc(const char *src, const char *dest) {
file_attr a; file_attr a;
getattr(src, &a); getattr(src, &a);
if (S_ISDIR(a.st.st_mode)) { if (S_ISDIR(a.st.st_mode)) {
xmkdirs(dest, 0); xmkdirs(dest, 0);
clone_dir(xopen(src, O_RDONLY | O_CLOEXEC), xopen(dest, O_RDONLY | O_CLOEXEC)); clone_dir(xopen(src, O_RDONLY | O_CLOEXEC), xopen(dest, O_RDONLY | O_CLOEXEC));
} else{ } else{
unlink(dest); unlink(dest);
if (S_ISREG(a.st.st_mode)) { if (S_ISREG(a.st.st_mode)) {
int sfd = xopen(src, O_RDONLY | O_CLOEXEC); int sfd = xopen(src, O_RDONLY | O_CLOEXEC);
int dfd = xopen(dest, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0); int dfd = xopen(dest, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0);
xsendfile(dfd, sfd, nullptr, a.st.st_size); xsendfile(dfd, sfd, nullptr, a.st.st_size);
close(sfd); close(sfd);
close(dfd); close(dfd);
} else if (S_ISLNK(a.st.st_mode)) { } else if (S_ISLNK(a.st.st_mode)) {
char buf[4096]; char buf[4096];
xreadlink(src, buf, sizeof(buf)); xreadlink(src, buf, sizeof(buf));
xsymlink(buf, dest); xsymlink(buf, dest);
} }
} }
setattr(dest, &a); setattr(dest, &a);
} }
void clone_dir(int src, int dest) { void clone_dir(int src, int dest) {
auto dir = xopen_dir(src); auto dir = xopen_dir(src);
run_finally f([&]{ close(dest); }); run_finally f([&]{ close(dest); });
for (dirent *entry; (entry = xreaddir(dir.get()));) { for (dirent *entry; (entry = xreaddir(dir.get()));) {
file_attr a; file_attr a;
getattrat(src, entry->d_name, &a); getattrat(src, entry->d_name, &a);
switch (entry->d_type) { switch (entry->d_type) {
case DT_DIR: { case DT_DIR: {
xmkdirat(dest, entry->d_name, 0); xmkdirat(dest, entry->d_name, 0);
setattrat(dest, entry->d_name, &a); setattrat(dest, entry->d_name, &a);
int sfd = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC); int sfd = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
int dst = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC); int dst = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
clone_dir(sfd, dst); clone_dir(sfd, dst);
break; break;
} }
case DT_REG: { case DT_REG: {
int sfd = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC); 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); int dfd = xopenat(dest, entry->d_name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0);
xsendfile(dfd, sfd, nullptr, a.st.st_size); xsendfile(dfd, sfd, nullptr, a.st.st_size);
fsetattr(dfd, &a); fsetattr(dfd, &a);
close(dfd); close(dfd);
close(sfd); close(sfd);
break; break;
} }
case DT_LNK: { case DT_LNK: {
char buf[4096]; char buf[4096];
xreadlinkat(src, entry->d_name, buf, sizeof(buf)); xreadlinkat(src, entry->d_name, buf, sizeof(buf));
xsymlinkat(buf, dest, entry->d_name); xsymlinkat(buf, dest, entry->d_name);
setattrat(dest, entry->d_name, &a); setattrat(dest, entry->d_name, &a);
break; break;
} }
} }
} }
} }
void link_path(const char *src, const char *dest) { void link_path(const char *src, const char *dest) {
link_dir(xopen(src, O_RDONLY | O_CLOEXEC), xopen(dest, O_RDONLY | O_CLOEXEC)); link_dir(xopen(src, O_RDONLY | O_CLOEXEC), xopen(dest, O_RDONLY | O_CLOEXEC));
} }
void link_dir(int src, int dest) { void link_dir(int src, int dest) {
auto dir = xopen_dir(src); auto dir = xopen_dir(src);
run_finally f([&]{ close(dest); }); run_finally f([&]{ close(dest); });
for (dirent *entry; (entry = xreaddir(dir.get()));) { for (dirent *entry; (entry = xreaddir(dir.get()));) {
if (entry->d_type == DT_DIR) { if (entry->d_type == DT_DIR) {
file_attr a; file_attr a;
getattrat(src, entry->d_name, &a); getattrat(src, entry->d_name, &a);
xmkdirat(dest, entry->d_name, 0); xmkdirat(dest, entry->d_name, 0);
setattrat(dest, entry->d_name, &a); setattrat(dest, entry->d_name, &a);
int sfd = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC); int sfd = xopenat(src, entry->d_name, O_RDONLY | O_CLOEXEC);
int dfd = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC); int dfd = xopenat(dest, entry->d_name, O_RDONLY | O_CLOEXEC);
link_dir(sfd, dfd); link_dir(sfd, dfd);
} else { } else {
xlinkat(src, entry->d_name, dest, entry->d_name, 0); xlinkat(src, entry->d_name, dest, entry->d_name, 0);
} }
} }
} }
int getattr(const char *path, file_attr *a) { int getattr(const char *path, file_attr *a) {
if (xlstat(path, &a->st) == -1) if (xlstat(path, &a->st) == -1)
return -1; return -1;
char *con; char *con;
if (lgetfilecon(path, &con) == -1) if (lgetfilecon(path, &con) == -1)
return -1; return -1;
strcpy(a->con, con); strcpy(a->con, con);
freecon(con); freecon(con);
return 0; return 0;
} }
int getattrat(int dirfd, const char *name, file_attr *a) { int getattrat(int dirfd, const char *name, file_attr *a) {
char path[4096]; char path[4096];
fd_pathat(dirfd, name, path, sizeof(path)); fd_pathat(dirfd, name, path, sizeof(path));
return getattr(path, a); return getattr(path, a);
} }
int fgetattr(int fd, file_attr *a) { int fgetattr(int fd, file_attr *a) {
if (xfstat(fd, &a->st) < 0) if (xfstat(fd, &a->st) < 0)
return -1; return -1;
char *con; char *con;
if (fgetfilecon(fd, &con) < 0) if (fgetfilecon(fd, &con) < 0)
return -1; return -1;
strcpy(a->con, con); strcpy(a->con, con);
freecon(con); freecon(con);
return 0; return 0;
} }
int setattr(const char *path, file_attr *a) { int setattr(const char *path, file_attr *a) {
if (chmod(path, a->st.st_mode & 0777) < 0) if (chmod(path, a->st.st_mode & 0777) < 0)
return -1; return -1;
if (chown(path, a->st.st_uid, a->st.st_gid) < 0) if (chown(path, a->st.st_uid, a->st.st_gid) < 0)
return -1; return -1;
if (a->con[0] && lsetfilecon(path, a->con) < 0) if (a->con[0] && lsetfilecon(path, a->con) < 0)
return -1; return -1;
return 0; return 0;
} }
int setattrat(int dirfd, const char *name, file_attr *a) { int setattrat(int dirfd, const char *name, file_attr *a) {
char path[4096]; char path[4096];
fd_pathat(dirfd, name, path, sizeof(path)); fd_pathat(dirfd, name, path, sizeof(path));
return setattr(path, a); return setattr(path, a);
} }
int fsetattr(int fd, file_attr *a) { int fsetattr(int fd, file_attr *a) {
if (fchmod(fd, a->st.st_mode & 0777) < 0) if (fchmod(fd, a->st.st_mode & 0777) < 0)
return -1; return -1;
if (fchown(fd, a->st.st_uid, a->st.st_gid) < 0) if (fchown(fd, a->st.st_uid, a->st.st_gid) < 0)
return -1; return -1;
if (a->con[0] && fsetfilecon(fd, a->con) < 0) if (a->con[0] && fsetfilecon(fd, a->con) < 0)
return -1; return -1;
return 0; return 0;
} }
void clone_attr(const char *src, const char *dest) { void clone_attr(const char *src, const char *dest) {
file_attr a; file_attr a;
getattr(src, &a); getattr(src, &a);
setattr(dest, &a); setattr(dest, &a);
} }
void fclone_attr(int src, int dest) { void fclone_attr(int src, int dest) {
file_attr a; file_attr a;
fgetattr(src, &a); fgetattr(src, &a);
fsetattr(dest, &a); fsetattr(dest, &a);
} }
void *__mmap(const char *filename, size_t *size, bool rw) { void *__mmap(const char *filename, size_t *size, bool rw) {
int fd = xopen(filename, (rw ? O_RDWR : O_RDONLY) | O_CLOEXEC); int fd = xopen(filename, (rw ? O_RDWR : O_RDONLY) | O_CLOEXEC);
if (fd < 0) { if (fd < 0) {
*size = 0; *size = 0;
return nullptr; return nullptr;
} }
struct stat st; struct stat st;
void *buf; void *buf;
if (fstat(fd, &st)) { if (fstat(fd, &st)) {
*size = 0; *size = 0;
return nullptr; return nullptr;
} }
if (S_ISBLK(st.st_mode)) if (S_ISBLK(st.st_mode))
ioctl(fd, BLKGETSIZE64, size); ioctl(fd, BLKGETSIZE64, size);
else else
*size = st.st_size; *size = st.st_size;
buf = *size > 0 ? xmmap(nullptr, *size, PROT_READ | (rw ? PROT_WRITE : 0), MAP_SHARED, fd, 0) : nullptr; buf = *size > 0 ? xmmap(nullptr, *size, PROT_READ | (rw ? PROT_WRITE : 0), MAP_SHARED, fd, 0) : nullptr;
close(fd); close(fd);
return buf; return buf;
} }
void fd_full_read(int fd, void **buf, size_t *size) { void fd_full_read(int fd, void **buf, size_t *size) {
*size = lseek(fd, 0, SEEK_END); *size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET); lseek(fd, 0, SEEK_SET);
*buf = xmalloc(*size + 1); *buf = xmalloc(*size + 1);
xxread(fd, *buf, *size); xxread(fd, *buf, *size);
((char *) *buf)[*size] = '\0'; ((char *) *buf)[*size] = '\0';
} }
void full_read(const char *filename, void **buf, size_t *size) { void full_read(const char *filename, void **buf, size_t *size) {
int fd = xopen(filename, O_RDONLY | O_CLOEXEC); int fd = xopen(filename, O_RDONLY | O_CLOEXEC);
if (fd < 0) { if (fd < 0) {
*buf = nullptr; *buf = nullptr;
*size = 0; *size = 0;
return; return;
} }
fd_full_read(fd, buf, size); fd_full_read(fd, buf, size);
close(fd); close(fd);
} }
string fd_full_read(int fd) { string fd_full_read(int fd) {
char buf[4096]; char buf[4096];
string str; string str;
for (ssize_t len; (len = xread(fd, buf, sizeof(buf))) > 0;) for (ssize_t len; (len = xread(fd, buf, sizeof(buf))) > 0;)
str.insert(str.end(), buf, buf + len); str.insert(str.end(), buf, buf + len);
return str; return str;
} }
string full_read(const char *filename) { string full_read(const char *filename) {
int fd = xopen(filename, O_RDONLY | O_CLOEXEC); int fd = xopen(filename, O_RDONLY | O_CLOEXEC);
run_finally f([=]{ close(fd); }); run_finally f([=]{ close(fd); });
return fd < 0 ? "" : fd_full_read(fd); return fd < 0 ? "" : fd_full_read(fd);
} }
void write_zero(int fd, size_t size) { void write_zero(int fd, size_t size) {
char buf[4096] = {0}; char buf[4096] = {0};
size_t len; size_t len;
while (size > 0) { while (size > 0) {
len = sizeof(buf) > size ? size : sizeof(buf); len = sizeof(buf) > size ? size : sizeof(buf);
write(fd, buf, len); write(fd, buf, len);
size -= len; size -= len;
} }
} }
void file_readline(bool trim, const char *file, const function<bool(string_view)> &fn) { void file_readline(bool trim, const char *file, const function<bool(string_view)> &fn) {
FILE *fp = xfopen(file, "re"); FILE *fp = xfopen(file, "re");
if (fp == nullptr) if (fp == nullptr)
return; return;
size_t len = 1024; size_t len = 1024;
char *buf = (char *) malloc(len); char *buf = (char *) malloc(len);
char *start; char *start;
ssize_t read; ssize_t read;
while ((read = getline(&buf, &len, fp)) >= 0) { while ((read = getline(&buf, &len, fp)) >= 0) {
start = buf; start = buf;
if (trim) { if (trim) {
while (read && "\n\r "sv.find(buf[read - 1]) != string::npos) while (read && "\n\r "sv.find(buf[read - 1]) != string::npos)
--read; --read;
buf[read] = '\0'; buf[read] = '\0';
while (*start == ' ') while (*start == ' ')
++start; ++start;
} }
if (!fn(start)) if (!fn(start))
break; break;
} }
fclose(fp); fclose(fp);
free(buf); free(buf);
} }
void parse_prop_file(const char *file, const function<bool(string_view, string_view)> &fn) { void parse_prop_file(const char *file, const function<bool(string_view, string_view)> &fn) {
file_readline(true, file, [&](string_view line_view) -> bool { file_readline(true, file, [&](string_view line_view) -> bool {
char *line = (char *) line_view.data(); char *line = (char *) line_view.data();
if (line[0] == '#') if (line[0] == '#')
return true; return true;
char *eql = strchr(line, '='); char *eql = strchr(line, '=');
if (eql == nullptr || eql == line) if (eql == nullptr || eql == line)
return true; return true;
*eql = '\0'; *eql = '\0';
return fn(line, eql + 1); return fn(line, eql + 1);
}); });
} }
void parse_mnt(const char *file, const function<bool(mntent*)> &fn) { void parse_mnt(const char *file, const function<bool(mntent*)> &fn) {
auto fp = sFILE(setmntent(file, "re"), endmntent); auto fp = sFILE(setmntent(file, "re"), endmntent);
if (fp) { if (fp) {
mntent mentry{}; mntent mentry{};
char buf[4096]; char buf[4096];
while (getmntent_r(fp.get(), &mentry, buf, sizeof(buf))) { while (getmntent_r(fp.get(), &mentry, buf, sizeof(buf))) {
if (!fn(&mentry)) if (!fn(&mentry))
break; break;
} }
} }
} }
void backup_folder(const char *dir, vector<raw_file> &files) { void backup_folder(const char *dir, vector<raw_file> &files) {
char path[4096]; char path[4096];
xrealpath(dir, path); xrealpath(dir, path);
int len = strlen(path); int len = strlen(path);
pre_order_walk(xopen(dir, O_RDONLY), [&](int dfd, dirent *entry) -> bool { pre_order_walk(xopen(dir, O_RDONLY), [&](int dfd, dirent *entry) -> bool {
int fd = xopenat(dfd, entry->d_name, O_RDONLY); int fd = xopenat(dfd, entry->d_name, O_RDONLY);
if (fd < 0) if (fd < 0)
return false; return false;
run_finally f([&]{ close(fd); }); run_finally f([&]{ close(fd); });
if (fd_path(fd, path, sizeof(path)) < 0) if (fd_path(fd, path, sizeof(path)) < 0)
return false; return false;
raw_file file; raw_file file;
file.path = path + len + 1; file.path = path + len + 1;
if (fgetattr(fd, &file.attr) < 0) if (fgetattr(fd, &file.attr) < 0)
return false; return false;
if (entry->d_type == DT_REG) { if (entry->d_type == DT_REG) {
fd_full_read(fd, file.buf, file.sz); fd_full_read(fd, file.buf, file.sz);
} else if (entry->d_type == DT_LNK) { } else if (entry->d_type == DT_LNK) {
xreadlinkat(dfd, entry->d_name, path, sizeof(path)); xreadlinkat(dfd, entry->d_name, path, sizeof(path));
file.sz = strlen(path) + 1; file.sz = strlen(path) + 1;
file.buf = (uint8_t *) xmalloc(file.sz); file.buf = (uint8_t *) xmalloc(file.sz);
memcpy(file.buf, path, file.sz); memcpy(file.buf, path, file.sz);
} }
files.emplace_back(std::move(file)); files.emplace_back(std::move(file));
return true; return true;
}); });
} }
void restore_folder(const char *dir, vector<raw_file> &files) { void restore_folder(const char *dir, vector<raw_file> &files) {
string base(dir); string base(dir);
// Pre-order means folders will always be first // Pre-order means folders will always be first
for (raw_file &file : files) { for (raw_file &file : files) {
string path = base + "/" + file.path; string path = base + "/" + file.path;
if (S_ISDIR(file.attr.st.st_mode)) { if (S_ISDIR(file.attr.st.st_mode)) {
mkdirs(path.data(), 0); mkdirs(path.data(), 0);
} else if (S_ISREG(file.attr.st.st_mode)) { } else if (S_ISREG(file.attr.st.st_mode)) {
auto fp = xopen_file(path.data(), "we"); auto fp = xopen_file(path.data(), "we");
fwrite(file.buf, 1, file.sz, fp.get()); fwrite(file.buf, 1, file.sz, fp.get());
} else if (S_ISLNK(file.attr.st.st_mode)) { } else if (S_ISLNK(file.attr.st.st_mode)) {
symlink((char *)file.buf, path.data()); symlink((char *)file.buf, path.data());
} }
setattr(path.data(), &file.attr); setattr(path.data(), &file.attr);
} }
} }
sDIR make_dir(DIR *dp) { sDIR make_dir(DIR *dp) {
return sDIR(dp, [](DIR *dp){ return dp ? closedir(dp) : 1; }); return sDIR(dp, [](DIR *dp){ return dp ? closedir(dp) : 1; });
} }
sFILE make_file(FILE *fp) { sFILE make_file(FILE *fp) {
return sFILE(fp, [](FILE *fp){ return fp ? fclose(fp) : 1; }); return sFILE(fp, [](FILE *fp){ return fp ? fclose(fp) : 1; });
} }

View File

@@ -14,29 +14,29 @@
#define align_off(p, a) (do_align(p, a) - (p)) #define align_off(p, a) (do_align(p, a) - (p))
struct file_attr { struct file_attr {
struct stat st; struct stat st;
char con[128]; char con[128];
}; };
struct raw_file { struct raw_file {
std::string path; std::string path;
file_attr attr; file_attr attr;
uint8_t *buf = nullptr; uint8_t *buf = nullptr;
size_t sz = 0; size_t sz = 0;
raw_file() = default; raw_file() = default;
raw_file(const raw_file&) = delete; raw_file(const raw_file&) = delete;
raw_file(raw_file &&d) { raw_file(raw_file &&d) {
path = std::move(d.path); path = std::move(d.path);
attr = d.attr; attr = d.attr;
buf = d.buf; buf = d.buf;
sz = d.sz; sz = d.sz;
d.buf = nullptr; d.buf = nullptr;
d.sz = 0; d.sz = 0;
} }
~raw_file() { ~raw_file() {
free(buf); free(buf);
} }
}; };
ssize_t fd_path(int fd, char *path, size_t size); ssize_t fd_path(int fd, char *path, size_t size);
@@ -63,11 +63,11 @@ std::string full_read(const char *filename);
void write_zero(int fd, size_t size); void write_zero(int fd, size_t size);
void file_readline(bool trim, const char *file, const std::function<bool(std::string_view)> &fn); void file_readline(bool trim, const char *file, const std::function<bool(std::string_view)> &fn);
static inline void file_readline(const char *file, static inline void file_readline(const char *file,
const std::function<bool(std::string_view)> &fn) { const std::function<bool(std::string_view)> &fn) {
file_readline(false, file, fn); file_readline(false, file, fn);
} }
void parse_prop_file(const char *file, void parse_prop_file(const char *file,
const std::function<bool(std::string_view, std::string_view)> &fn); const std::function<bool(std::string_view, std::string_view)> &fn);
void *__mmap(const char *filename, size_t *size, bool rw); void *__mmap(const char *filename, size_t *size, bool rw);
void frm_rf(int dirfd); void frm_rf(int dirfd);
void clone_dir(int src, int dest); void clone_dir(int src, int dest);
@@ -77,38 +77,38 @@ void restore_folder(const char *dir, std::vector<raw_file> &files);
template <typename T> template <typename T>
void full_read(const char *filename, T &buf, size_t &size) { void full_read(const char *filename, T &buf, size_t &size) {
static_assert(std::is_pointer<T>::value); static_assert(std::is_pointer<T>::value);
full_read(filename, reinterpret_cast<void**>(&buf), &size); full_read(filename, reinterpret_cast<void**>(&buf), &size);
} }
template <typename T> template <typename T>
void fd_full_read(int fd, T &buf, size_t &size) { void fd_full_read(int fd, T &buf, size_t &size) {
static_assert(std::is_pointer<T>::value); static_assert(std::is_pointer<T>::value);
fd_full_read(fd, reinterpret_cast<void**>(&buf), &size); fd_full_read(fd, reinterpret_cast<void**>(&buf), &size);
} }
template <typename B> template <typename B>
void mmap_ro(const char *filename, B &buf, size_t &sz) { void mmap_ro(const char *filename, B &buf, size_t &sz) {
buf = (B) __mmap(filename, &sz, false); buf = (B) __mmap(filename, &sz, false);
} }
template <typename B, typename L> template <typename B, typename L>
void mmap_ro(const char *filename, B &buf, L &sz) { void mmap_ro(const char *filename, B &buf, L &sz) {
size_t __sz; size_t __sz;
buf = (B) __mmap(filename, &__sz, false); buf = (B) __mmap(filename, &__sz, false);
sz = __sz; sz = __sz;
} }
template <typename B> template <typename B>
void mmap_rw(const char *filename, B &buf, size_t &sz) { void mmap_rw(const char *filename, B &buf, size_t &sz) {
buf = (B) __mmap(filename, &sz, true); buf = (B) __mmap(filename, &sz, true);
} }
template <typename B, typename L> template <typename B, typename L>
void mmap_rw(const char *filename, B &buf, L &sz) { void mmap_rw(const char *filename, B &buf, L &sz) {
size_t __sz; size_t __sz;
buf = (B) __mmap(filename, &__sz, true); buf = (B) __mmap(filename, &__sz, true);
sz = __sz; sz = __sz;
} }
using sFILE = std::unique_ptr<FILE, decltype(&fclose)>; using sFILE = std::unique_ptr<FILE, decltype(&fclose)>;
@@ -117,25 +117,25 @@ sDIR make_dir(DIR *dp);
sFILE make_file(FILE *fp); sFILE make_file(FILE *fp);
static inline sDIR open_dir(const char *path) { static inline sDIR open_dir(const char *path) {
return make_dir(opendir(path)); return make_dir(opendir(path));
} }
static inline sDIR xopen_dir(const char *path) { static inline sDIR xopen_dir(const char *path) {
return make_dir(xopendir(path)); return make_dir(xopendir(path));
} }
static inline sDIR xopen_dir(int dirfd) { static inline sDIR xopen_dir(int dirfd) {
return make_dir(xfdopendir(dirfd)); return make_dir(xfdopendir(dirfd));
} }
static inline sFILE open_file(const char *path, const char *mode) { static inline sFILE open_file(const char *path, const char *mode) {
return make_file(fopen(path, mode)); return make_file(fopen(path, mode));
} }
static inline sFILE xopen_file(const char *path, const char *mode) { static inline sFILE xopen_file(const char *path, const char *mode) {
return make_file(xfopen(path, mode)); return make_file(xfopen(path, mode));
} }
static inline sFILE xopen_file(int fd, const char *mode) { static inline sFILE xopen_file(int fd, const char *mode) {
return make_file(xfdopen(fd, mode)); return make_file(xfdopen(fd, mode));
} }

View File

@@ -11,67 +11,67 @@
struct cpio_newc_header; struct cpio_newc_header;
struct cpio_entry_base { struct cpio_entry_base {
uint32_t mode = 0; uint32_t mode = 0;
uint32_t uid = 0; uint32_t uid = 0;
uint32_t gid = 0; uint32_t gid = 0;
uint32_t filesize = 0; uint32_t filesize = 0;
void *data = nullptr; void *data = nullptr;
cpio_entry_base() : mode(0), uid(0), gid(0), filesize(0) {}; cpio_entry_base() : mode(0), uid(0), gid(0), filesize(0) {};
explicit cpio_entry_base(const cpio_newc_header *h); explicit cpio_entry_base(const cpio_newc_header *h);
virtual ~cpio_entry_base() = default; virtual ~cpio_entry_base() = default;
}; };
struct cpio_entry : public cpio_entry_base { struct cpio_entry : public cpio_entry_base {
std::string filename; std::string filename;
cpio_entry() = default; cpio_entry() = default;
explicit cpio_entry(const char *name, uint32_t mode) : filename(name) { explicit cpio_entry(const char *name, uint32_t mode) : filename(name) {
this->mode = mode; this->mode = mode;
} }
explicit cpio_entry(const cpio_newc_header *h) : cpio_entry_base(h) {} explicit cpio_entry(const cpio_newc_header *h) : cpio_entry_base(h) {}
~cpio_entry() override { free(data); }; ~cpio_entry() override { free(data); };
}; };
typedef std::map<std::string_view, std::unique_ptr<cpio_entry_base>> entry_map; typedef std::map<std::string_view, std::unique_ptr<cpio_entry_base>> entry_map;
class cpio { class cpio {
public: public:
void dump(const char *file); void dump(const char *file);
void rm(const char *name, bool r = false); void rm(const char *name, bool r = false);
void extract(); void extract();
bool extract(const char *name, const char *file); bool extract(const char *name, const char *file);
bool exists(const char *name); bool exists(const char *name);
protected: protected:
entry_map entries; entry_map entries;
void rm(entry_map::iterator &it); void rm(entry_map::iterator &it);
void dump(FILE *out); void dump(FILE *out);
}; };
class cpio_rw : public cpio { class cpio_rw : public cpio {
public: public:
cpio_rw() = default; cpio_rw() = default;
explicit cpio_rw(const char *file); explicit cpio_rw(const char *file);
void load_cpio(const char *file); void load_cpio(const char *file);
void add(mode_t mode, const char *name, const char *file); void add(mode_t mode, const char *name, const char *file);
void mkdir(mode_t mode, const char *name); void mkdir(mode_t mode, const char *name);
void ln(const char *target, const char *name); void ln(const char *target, const char *name);
bool mv(const char *from, const char *to); bool mv(const char *from, const char *to);
protected: protected:
void insert(cpio_entry *e); void insert(cpio_entry *e);
void mv(entry_map::iterator &it, const char *to); void mv(entry_map::iterator &it, const char *to);
void load_cpio(const char *buf, size_t sz); void load_cpio(const char *buf, size_t sz);
}; };
class cpio_mmap : public cpio { class cpio_mmap : public cpio {
public: public:
explicit cpio_mmap(const char *file); explicit cpio_mmap(const char *file);
~cpio_mmap(); ~cpio_mmap();
private: private:
char *buf; char *buf;
size_t sz; size_t sz;
}; };

View File

@@ -7,10 +7,10 @@
class stream { class stream {
public: public:
virtual int read(void *buf, size_t len); virtual int read(void *buf, size_t len);
virtual int write(const void *buf, size_t len); virtual int write(const void *buf, size_t len);
virtual off_t seek(off_t off, int whence); virtual off_t seek(off_t off, int whence);
virtual ~stream() = default; virtual ~stream() = default;
}; };
using stream_ptr = std::unique_ptr<stream>; using stream_ptr = std::unique_ptr<stream>;
@@ -18,44 +18,44 @@ using stream_ptr = std::unique_ptr<stream>;
// Delegates all operations to base stream // Delegates all operations to base stream
class filter_stream : public stream { class filter_stream : public stream {
public: public:
filter_stream(stream_ptr &&base) : base(std::move(base)) {} filter_stream(stream_ptr &&base) : base(std::move(base)) {}
int read(void *buf, size_t len) override; int read(void *buf, size_t len) override;
int write(const void *buf, size_t len) override; int write(const void *buf, size_t len) override;
protected: protected:
stream_ptr base; stream_ptr base;
}; };
// Byte stream that dynamically allocates memory // Byte stream that dynamically allocates memory
class byte_stream : public stream { class byte_stream : public stream {
public: public:
byte_stream(uint8_t *&buf, size_t &len); byte_stream(uint8_t *&buf, size_t &len);
template <class byte> template <class byte>
byte_stream(byte *&buf, size_t &len) : byte_stream(reinterpret_cast<uint8_t *&>(buf), len) {} byte_stream(byte *&buf, size_t &len) : byte_stream(reinterpret_cast<uint8_t *&>(buf), len) {}
int read(void *buf, size_t len) override; int read(void *buf, size_t len) override;
int write(const void *buf, size_t len) override; int write(const void *buf, size_t len) override;
off_t seek(off_t off, int whence) override; off_t seek(off_t off, int whence) override;
private: private:
uint8_t *&_buf; uint8_t *&_buf;
size_t &_len; size_t &_len;
size_t _pos = 0; size_t _pos = 0;
size_t _cap = 0; size_t _cap = 0;
void resize(size_t new_pos, bool zero = false); void resize(size_t new_pos, bool zero = false);
}; };
// File stream but does not close the file descriptor at any time // File stream but does not close the file descriptor at any time
class fd_stream : public stream { class fd_stream : public stream {
public: public:
fd_stream(int fd) : fd(fd) {} fd_stream(int fd) : fd(fd) {}
int read(void *buf, size_t len) override; int read(void *buf, size_t len) override;
int write(const void *buf, size_t len) override; int write(const void *buf, size_t len) override;
off_t seek(off_t off, int whence) override; off_t seek(off_t off, int whence) override;
private: private:
int fd; int fd;
}; };
/* **************************************** /* ****************************************
@@ -65,14 +65,14 @@ private:
// sFILE -> stream_ptr // sFILE -> stream_ptr
class fp_stream final : public stream { class fp_stream final : public stream {
public: public:
fp_stream(FILE *fp = nullptr) : fp(fp, fclose) {} fp_stream(FILE *fp = nullptr) : fp(fp, fclose) {}
fp_stream(sFILE &&fp) : fp(std::move(fp)) {} fp_stream(sFILE &&fp) : fp(std::move(fp)) {}
int read(void *buf, size_t len) override; int read(void *buf, size_t len) override;
int write(const void *buf, size_t len) override; int write(const void *buf, size_t len) override;
off_t seek(off_t off, int whence) override; off_t seek(off_t off, int whence) override;
private: private:
sFILE fp; sFILE fp;
}; };
// stream_ptr -> sFILE // stream_ptr -> sFILE
@@ -80,5 +80,5 @@ sFILE make_stream_fp(stream_ptr &&strm);
template <class T, class... Args> template <class T, class... Args>
sFILE make_stream_fp(Args &&... args) { sFILE make_stream_fp(Args &&... args) {
return make_stream_fp(stream_ptr(new T(std::forward<Args>(args)...))); return make_stream_fp(stream_ptr(new T(std::forward<Args>(args)...)));
} }

View File

@@ -11,48 +11,48 @@ int nop_log(const char *, va_list) { return 0; }
void nop_ex(int) {} void nop_ex(int) {}
log_callback log_cb = { log_callback log_cb = {
.d = nop_log, .d = nop_log,
.i = nop_log, .i = nop_log,
.w = nop_log, .w = nop_log,
.e = nop_log, .e = nop_log,
.ex = nop_ex .ex = nop_ex
}; };
void no_logging() { void no_logging() {
log_cb.d = nop_log; log_cb.d = nop_log;
log_cb.i = nop_log; log_cb.i = nop_log;
log_cb.w = nop_log; log_cb.w = nop_log;
log_cb.e = nop_log; log_cb.e = nop_log;
log_cb.ex = nop_ex; log_cb.ex = nop_ex;
} }
static int vprintfe(const char *fmt, va_list ap) { static int vprintfe(const char *fmt, va_list ap) {
return vfprintf(stderr, fmt, ap); return vfprintf(stderr, fmt, ap);
} }
void cmdline_logging() { void cmdline_logging() {
log_cb.d = vprintfe; log_cb.d = vprintfe;
log_cb.i = vprintf; log_cb.i = vprintf;
log_cb.w = vprintfe; log_cb.w = vprintfe;
log_cb.e = vprintfe; log_cb.e = vprintfe;
log_cb.ex = exit; log_cb.ex = exit;
} }
template <int type> template <int type>
void log_handler(const char *fmt, ...) { void log_handler(const char *fmt, ...) {
va_list argv; va_list argv;
va_start(argv, fmt); va_start(argv, fmt);
if constexpr (type == L_DEBUG) { if constexpr (type == L_DEBUG) {
log_cb.d(fmt, argv); log_cb.d(fmt, argv);
} else if constexpr (type == L_INFO) { } else if constexpr (type == L_INFO) {
log_cb.i(fmt, argv); log_cb.i(fmt, argv);
} else if constexpr (type == L_WARN) { } else if constexpr (type == L_WARN) {
log_cb.w(fmt, argv); log_cb.w(fmt, argv);
} else if constexpr (type == L_ERR) { } else if constexpr (type == L_ERR) {
log_cb.e(fmt, argv); log_cb.e(fmt, argv);
log_cb.ex(1); log_cb.ex(1);
} }
va_end(argv); va_end(argv);
} }
template void log_handler<L_INFO>(const char *fmt, ...); template void log_handler<L_INFO>(const char *fmt, ...);

View File

@@ -5,18 +5,18 @@
#include <cstring> #include <cstring>
enum { enum {
L_DEBUG, L_DEBUG,
L_INFO, L_INFO,
L_WARN, L_WARN,
L_ERR L_ERR
}; };
struct log_callback { struct log_callback {
int (*d)(const char* fmt, va_list ap); int (*d)(const char* fmt, va_list ap);
int (*i)(const char* fmt, va_list ap); int (*i)(const char* fmt, va_list ap);
int (*w)(const char* fmt, va_list ap); int (*w)(const char* fmt, va_list ap);
int (*e)(const char* fmt, va_list ap); int (*e)(const char* fmt, va_list ap);
void (*ex)(int code); void (*ex)(int code);
}; };
extern log_callback log_cb; extern log_callback log_cb;

View File

@@ -14,23 +14,23 @@
using namespace std; using namespace std;
int fork_dont_care() { int fork_dont_care() {
if (int pid = xfork()) { if (int pid = xfork()) {
waitpid(pid, nullptr, 0); waitpid(pid, nullptr, 0);
return pid; return pid;
} else if (xfork()) { } else if (xfork()) {
exit(0); exit(0);
} }
return 0; return 0;
} }
int fork_no_orphan() { int fork_no_orphan() {
int pid = xfork(); int pid = xfork();
if (pid) if (pid)
return pid; return pid;
prctl(PR_SET_PDEATHSIG, SIGTERM); prctl(PR_SET_PDEATHSIG, SIGTERM);
if (getppid() == 1) if (getppid() == 1)
exit(1); exit(1);
return 0; return 0;
} }
constexpr char ALPHANUM[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; constexpr char ALPHANUM[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
@@ -38,111 +38,111 @@ static bool seeded = false;
static std::mt19937 gen; static std::mt19937 gen;
static std::uniform_int_distribution<int> dist(0, sizeof(ALPHANUM) - 2); static std::uniform_int_distribution<int> dist(0, sizeof(ALPHANUM) - 2);
int gen_rand_str(char *buf, int len, bool varlen) { int gen_rand_str(char *buf, int len, bool varlen) {
if (!seeded) { if (!seeded) {
if (access("/dev/urandom", F_OK) != 0) if (access("/dev/urandom", F_OK) != 0)
mknod("/dev/urandom", 0600 | S_IFCHR, makedev(1, 9)); mknod("/dev/urandom", 0600 | S_IFCHR, makedev(1, 9));
int fd = xopen("/dev/urandom", O_RDONLY | O_CLOEXEC); int fd = xopen("/dev/urandom", O_RDONLY | O_CLOEXEC);
unsigned seed; unsigned seed;
xxread(fd, &seed, sizeof(seed)); xxread(fd, &seed, sizeof(seed));
gen.seed(seed); gen.seed(seed);
close(fd); close(fd);
seeded = true; seeded = true;
} }
if (varlen) { if (varlen) {
std::uniform_int_distribution<int> len_dist(len / 2, len); std::uniform_int_distribution<int> len_dist(len / 2, len);
len = len_dist(gen); len = len_dist(gen);
} }
for (int i = 0; i < len - 1; ++i) for (int i = 0; i < len - 1; ++i)
buf[i] = ALPHANUM[dist(gen)]; buf[i] = ALPHANUM[dist(gen)];
buf[len - 1] = '\0'; buf[len - 1] = '\0';
return len - 1; return len - 1;
} }
int exec_command(exec_t &exec) { int exec_command(exec_t &exec) {
int pipefd[] = {-1, -1}; int pipefd[] = {-1, -1};
int outfd = -1; int outfd = -1;
if (exec.fd == -1) { if (exec.fd == -1) {
if (xpipe2(pipefd, O_CLOEXEC) == -1) if (xpipe2(pipefd, O_CLOEXEC) == -1)
return -1; return -1;
outfd = pipefd[1]; outfd = pipefd[1];
} else if (exec.fd >= 0) { } else if (exec.fd >= 0) {
outfd = exec.fd; outfd = exec.fd;
} }
int pid = exec.fork(); int pid = exec.fork();
if (pid < 0) { if (pid < 0) {
close(pipefd[0]); close(pipefd[0]);
close(pipefd[1]); close(pipefd[1]);
return -1; return -1;
} else if (pid) { } else if (pid) {
if (exec.fd == -1) { if (exec.fd == -1) {
exec.fd = pipefd[0]; exec.fd = pipefd[0];
close(pipefd[1]); close(pipefd[1]);
} }
return pid; return pid;
} }
// Unblock all signals // Unblock all signals
sigset_t set; sigset_t set;
sigfillset(&set); sigfillset(&set);
pthread_sigmask(SIG_UNBLOCK, &set, nullptr); pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
if (outfd >= 0) { if (outfd >= 0) {
xdup2(outfd, STDOUT_FILENO); xdup2(outfd, STDOUT_FILENO);
if (exec.err) if (exec.err)
xdup2(outfd, STDERR_FILENO); xdup2(outfd, STDERR_FILENO);
close(outfd); close(outfd);
} }
// Call the pre-exec callback // Call the pre-exec callback
if (exec.pre_exec) if (exec.pre_exec)
exec.pre_exec(); exec.pre_exec();
execve(exec.argv[0], (char **) exec.argv, environ); execve(exec.argv[0], (char **) exec.argv, environ);
PLOGE("execve %s", exec.argv[0]); PLOGE("execve %s", exec.argv[0]);
exit(-1); exit(-1);
} }
int exec_command_sync(exec_t &exec) { int exec_command_sync(exec_t &exec) {
int pid = exec_command(exec); int pid = exec_command(exec);
if (pid < 0) if (pid < 0)
return -1; return -1;
int status; int status;
waitpid(pid, &status, 0); waitpid(pid, &status, 0);
return WEXITSTATUS(status); return WEXITSTATUS(status);
} }
int new_daemon_thread(thread_entry entry, void *arg, const pthread_attr_t *attr) { int new_daemon_thread(thread_entry entry, void *arg, const pthread_attr_t *attr) {
pthread_t thread; pthread_t thread;
int ret = xpthread_create(&thread, attr, entry, arg); int ret = xpthread_create(&thread, attr, entry, arg);
if (ret == 0) if (ret == 0)
pthread_detach(thread); pthread_detach(thread);
return ret; return ret;
} }
static void *proxy_routine(void *fp) { static void *proxy_routine(void *fp) {
auto fn = reinterpret_cast<std::function<void()>*>(fp); auto fn = reinterpret_cast<std::function<void()>*>(fp);
(*fn)(); (*fn)();
delete fn; delete fn;
return nullptr; return nullptr;
} }
int new_daemon_thread(std::function<void()> &&entry) { int new_daemon_thread(std::function<void()> &&entry) {
return new_daemon_thread(proxy_routine, new std::function<void()>(std::move(entry))); return new_daemon_thread(proxy_routine, new std::function<void()>(std::move(entry)));
} }
static char *argv0; static char *argv0;
static size_t name_len; static size_t name_len;
void init_argv0(int argc, char **argv) { void init_argv0(int argc, char **argv) {
argv0 = argv[0]; argv0 = argv[0];
name_len = (argv[argc - 1] - argv[0]) + strlen(argv[argc - 1]) + 1; name_len = (argv[argc - 1] - argv[0]) + strlen(argv[argc - 1]) + 1;
} }
void set_nice_name(const char *name) { void set_nice_name(const char *name) {
memset(argv0, 0, name_len); memset(argv0, 0, name_len);
strlcpy(argv0, name, name_len); strlcpy(argv0, name, name_len);
prctl(PR_SET_NAME, name); prctl(PR_SET_NAME, name);
} }
/* /*
@@ -150,52 +150,52 @@ void set_nice_name(const char *name) {
* Use our own implementation for faster conversion. * Use our own implementation for faster conversion.
*/ */
int parse_int(const char *s) { int parse_int(const char *s) {
int val = 0; int val = 0;
char c; char c;
while ((c = *(s++))) { while ((c = *(s++))) {
if (c > '9' || c < '0') if (c > '9' || c < '0')
return -1; return -1;
val = val * 10 + c - '0'; val = val * 10 + c - '0';
} }
return val; return val;
} }
uint32_t binary_gcd(uint32_t u, uint32_t v) { uint32_t binary_gcd(uint32_t u, uint32_t v) {
if (u == 0) return v; if (u == 0) return v;
if (v == 0) return u; if (v == 0) return u;
auto shift = __builtin_ctz(u | v); auto shift = __builtin_ctz(u | v);
u >>= __builtin_ctz(u); u >>= __builtin_ctz(u);
do { do {
v >>= __builtin_ctz(v); v >>= __builtin_ctz(v);
if (u > v) { if (u > v) {
auto t = v; auto t = v;
v = u; v = u;
u = t; u = t;
} }
v -= u; v -= u;
} while (v != 0); } while (v != 0);
return u << shift; return u << shift;
} }
int switch_mnt_ns(int pid) { int switch_mnt_ns(int pid) {
char mnt[32]; char mnt[32];
snprintf(mnt, sizeof(mnt), "/proc/%d/ns/mnt", pid); snprintf(mnt, sizeof(mnt), "/proc/%d/ns/mnt", pid);
if (access(mnt, R_OK) == -1) return 1; // Maybe process died.. if (access(mnt, R_OK) == -1) return 1; // Maybe process died..
int fd, ret; int fd, ret;
fd = xopen(mnt, O_RDONLY); fd = xopen(mnt, O_RDONLY);
if (fd < 0) return 1; if (fd < 0) return 1;
// Switch to its namespace // Switch to its namespace
ret = xsetns(fd, 0); ret = xsetns(fd, 0);
close(fd); close(fd);
return ret; return ret;
} }
string &replace_all(string &str, string_view from, string_view to) { string &replace_all(string &str, string_view from, string_view to) {
size_t pos = 0; size_t pos = 0;
while((pos = str.find(from, pos)) != string::npos) { while((pos = str.find(from, pos)) != string::npos) {
str.replace(pos, from.length(), to); str.replace(pos, from.length(), to);
pos += to.length(); pos += to.length();
} }
return str; return str;
} }

View File

@@ -10,48 +10,48 @@
class mutex_guard { class mutex_guard {
public: public:
explicit mutex_guard(pthread_mutex_t &m): mutex(&m) { explicit mutex_guard(pthread_mutex_t &m): mutex(&m) {
pthread_mutex_lock(mutex); pthread_mutex_lock(mutex);
} }
explicit mutex_guard(pthread_mutex_t *m): mutex(m) { explicit mutex_guard(pthread_mutex_t *m): mutex(m) {
pthread_mutex_lock(mutex); pthread_mutex_lock(mutex);
} }
~mutex_guard() { ~mutex_guard() {
pthread_mutex_unlock(mutex); pthread_mutex_unlock(mutex);
} }
private: private:
pthread_mutex_t *mutex; pthread_mutex_t *mutex;
}; };
template <class Func> template <class Func>
class run_finally { class run_finally {
public: public:
explicit run_finally(const Func &fn) : fn(fn) {} explicit run_finally(const Func &fn) : fn(fn) {}
~run_finally() { fn(); } ~run_finally() { fn(); }
private: private:
const Func &fn; const Func &fn;
}; };
template <typename T> template <typename T>
class reversed_container { class reversed_container {
public: public:
reversed_container(T &base) : base(base) {} reversed_container(T &base) : base(base) {}
decltype(std::declval<T>().rbegin()) begin() { return base.rbegin(); } decltype(std::declval<T>().rbegin()) begin() { return base.rbegin(); }
decltype(std::declval<T>().crbegin()) begin() const { return base.crbegin(); } decltype(std::declval<T>().crbegin()) begin() const { return base.crbegin(); }
decltype(std::declval<T>().crbegin()) cbegin() 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>().rend()) end() { return base.rend(); }
decltype(std::declval<T>().crend()) end() const { return base.crend(); } decltype(std::declval<T>().crend()) end() const { return base.crend(); }
decltype(std::declval<T>().crend()) cend() const { return base.crend(); } decltype(std::declval<T>().crend()) cend() const { return base.crend(); }
private: private:
T &base; T &base;
}; };
template <typename T> template <typename T>
reversed_container<T> reversed(T &base) { reversed_container<T> reversed(T &base) {
return reversed_container<T>(base); return reversed_container<T>(base);
} }
int parse_int(const char *s); int parse_int(const char *s);
@@ -63,13 +63,13 @@ int new_daemon_thread(thread_entry entry, void *arg = nullptr, const pthread_att
int new_daemon_thread(std::function<void()> &&entry); int new_daemon_thread(std::function<void()> &&entry);
static inline bool str_contains(std::string_view s, std::string_view ss) { static inline bool str_contains(std::string_view s, std::string_view ss) {
return s.find(ss) != std::string::npos; return s.find(ss) != std::string::npos;
} }
static inline bool str_starts(std::string_view s, std::string_view ss) { static inline bool str_starts(std::string_view s, std::string_view ss) {
return s.rfind(ss, 0) == 0; return s.rfind(ss, 0) == 0;
} }
static inline bool str_ends(std::string_view s, std::string_view ss) { 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; return s.size() >= ss.size() && s.compare(s.size() - ss.size(), std::string::npos, ss) == 0;
} }
int fork_dont_care(); int fork_dont_care();
@@ -82,38 +82,38 @@ 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::string &replace_all(std::string &str, std::string_view from, std::string_view to);
struct exec_t { struct exec_t {
bool err = false; bool err = false;
int fd = -2; int fd = -2;
void (*pre_exec)() = nullptr; void (*pre_exec)() = nullptr;
int (*fork)() = xfork; int (*fork)() = xfork;
const char **argv = nullptr; const char **argv = nullptr;
}; };
int exec_command(exec_t &exec); int exec_command(exec_t &exec);
template <class ...Args> template <class ...Args>
int exec_command(exec_t &exec, Args &&...args) { int exec_command(exec_t &exec, Args &&...args) {
const char *argv[] = {args..., nullptr}; const char *argv[] = {args..., nullptr};
exec.argv = argv; exec.argv = argv;
return exec_command(exec); return exec_command(exec);
} }
int exec_command_sync(exec_t &exec); int exec_command_sync(exec_t &exec);
template <class ...Args> template <class ...Args>
int exec_command_sync(exec_t &exec, Args &&...args) { int exec_command_sync(exec_t &exec, Args &&...args) {
const char *argv[] = {args..., nullptr}; const char *argv[] = {args..., nullptr};
exec.argv = argv; exec.argv = argv;
return exec_command_sync(exec); return exec_command_sync(exec);
} }
template <class ...Args> template <class ...Args>
int exec_command_sync(Args &&...args) { int exec_command_sync(Args &&...args) {
exec_t exec{}; exec_t exec{};
return exec_command_sync(exec, args...); return exec_command_sync(exec, args...);
} }
template <class ...Args> template <class ...Args>
void exec_command_async(Args &&...args) { void exec_command_async(Args &&...args) {
const char *argv[] = {args..., nullptr}; const char *argv[] = {args..., nullptr};
exec_t exec { exec_t exec {
.argv = argv, .argv = argv,
.fork = fork_dont_care .fork = fork_dont_care
}; };
exec_command(exec); exec_command(exec);
} }

View File

@@ -14,96 +14,96 @@
* License: BSD, full copyright notice please check original source */ * License: BSD, full copyright notice please check original source */
ssize_t compat_getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) { ssize_t compat_getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) {
char *ptr, *eptr; char *ptr, *eptr;
if (*buf == nullptr || *bufsiz == 0) { if (*buf == nullptr || *bufsiz == 0) {
*bufsiz = BUFSIZ; *bufsiz = BUFSIZ;
if ((*buf = (char *) malloc(*bufsiz)) == nullptr) if ((*buf = (char *) malloc(*bufsiz)) == nullptr)
return -1; return -1;
} }
for (ptr = *buf, eptr = *buf + *bufsiz;;) { for (ptr = *buf, eptr = *buf + *bufsiz;;) {
int c = fgetc(fp); int c = fgetc(fp);
if (c == -1) { if (c == -1) {
if (feof(fp)) if (feof(fp))
return ptr == *buf ? -1 : ptr - *buf; return ptr == *buf ? -1 : ptr - *buf;
else else
return -1; return -1;
} }
*ptr++ = c; *ptr++ = c;
if (c == delimiter) { if (c == delimiter) {
*ptr = '\0'; *ptr = '\0';
return ptr - *buf; return ptr - *buf;
} }
if (ptr + 2 >= eptr) { if (ptr + 2 >= eptr) {
char *nbuf; char *nbuf;
size_t nbufsiz = *bufsiz * 2; size_t nbufsiz = *bufsiz * 2;
ssize_t d = ptr - *buf; ssize_t d = ptr - *buf;
if ((nbuf = (char *) realloc(*buf, nbufsiz)) == nullptr) if ((nbuf = (char *) realloc(*buf, nbufsiz)) == nullptr)
return -1; return -1;
*buf = nbuf; *buf = nbuf;
*bufsiz = nbufsiz; *bufsiz = nbufsiz;
eptr = nbuf + nbufsiz; eptr = nbuf + nbufsiz;
ptr = nbuf + d; ptr = nbuf + d;
} }
} }
} }
ssize_t compat_getline(char **buf, size_t *bufsiz, FILE *fp) { ssize_t compat_getline(char **buf, size_t *bufsiz, FILE *fp) {
return getdelim(buf, bufsiz, '\n', fp); return getdelim(buf, bufsiz, '\n', fp);
} }
/* Original source: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/mntent.cpp /* Original source: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/mntent.cpp
* License: AOSP, full copyright notice please check original source */ * License: AOSP, full copyright notice please check original source */
struct mntent *compat_getmntent_r(FILE* fp, struct mntent* e, char* buf, int buf_len) { struct mntent *compat_getmntent_r(FILE* fp, struct mntent* e, char* buf, int buf_len) {
memset(e, 0, sizeof(*e)); memset(e, 0, sizeof(*e));
while (fgets(buf, buf_len, fp) != nullptr) { while (fgets(buf, buf_len, fp) != nullptr) {
// Entries look like "proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0". // 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. // That is: mnt_fsname mnt_dir mnt_type mnt_opts 0 0.
int fsname0, fsname1, dir0, dir1, type0, type1, opts0, opts1; 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", 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, &fsname0, &fsname1, &dir0, &dir1, &type0, &type1, &opts0, &opts1,
&e->mnt_freq, &e->mnt_passno) == 2) { &e->mnt_freq, &e->mnt_passno) == 2) {
e->mnt_fsname = &buf[fsname0]; e->mnt_fsname = &buf[fsname0];
buf[fsname1] = '\0'; buf[fsname1] = '\0';
e->mnt_dir = &buf[dir0]; e->mnt_dir = &buf[dir0];
buf[dir1] = '\0'; buf[dir1] = '\0';
e->mnt_type = &buf[type0]; e->mnt_type = &buf[type0];
buf[type1] = '\0'; buf[type1] = '\0';
e->mnt_opts = &buf[opts0]; e->mnt_opts = &buf[opts0];
buf[opts1] = '\0'; buf[opts1] = '\0';
return e; return e;
} }
} }
return nullptr; return nullptr;
} }
FILE *compat_setmntent(const char* path, const char* mode) { FILE *compat_setmntent(const char* path, const char* mode) {
return fopen(path, mode); return fopen(path, mode);
} }
int compat_endmntent(FILE* fp) { int compat_endmntent(FILE* fp) {
if (fp != nullptr) { if (fp != nullptr) {
fclose(fp); fclose(fp);
} }
return 1; return 1;
} }
char *compat_hasmntopt(const struct mntent* mnt, const char* opt) { char *compat_hasmntopt(const struct mntent* mnt, const char* opt) {
char* token = mnt->mnt_opts; char* token = mnt->mnt_opts;
char* const end = mnt->mnt_opts + strlen(mnt->mnt_opts); char* const end = mnt->mnt_opts + strlen(mnt->mnt_opts);
const size_t optLen = strlen(opt); const size_t optLen = strlen(opt);
while (token) { while (token) {
char* const tokenEnd = token + optLen; char* const tokenEnd = token + optLen;
if (tokenEnd > end) break; if (tokenEnd > end) break;
if (memcmp(token, opt, optLen) == 0 && if (memcmp(token, opt, optLen) == 0 &&
(*tokenEnd == '\0' || *tokenEnd == ',' || *tokenEnd == '=')) { (*tokenEnd == '\0' || *tokenEnd == ',' || *tokenEnd == '=')) {
return token; return token;
} }
token = strchr(token, ','); token = strchr(token, ',');
if (token) token++; if (token) token++;
} }
return nullptr; return nullptr;
} }

View File

@@ -31,38 +31,38 @@ int compat_endmntent(FILE* fp);
char *compat_hasmntopt(const struct mntent* mnt, const char* opt); char *compat_hasmntopt(const struct mntent* mnt, const char* opt);
static inline int compat_setns(int fd, int nstype) { static inline int compat_setns(int fd, int nstype) {
return syscall(__NR_setns, fd, nstype); return syscall(__NR_setns, fd, nstype);
} }
static inline int compat_unshare(int flags) { static inline int compat_unshare(int flags) {
return syscall(__NR_unshare, flags); return syscall(__NR_unshare, flags);
} }
static inline int compat_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) { static inline int compat_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) {
return syscall(__NR_accept4, sockfd, addr, addrlen, flags); return syscall(__NR_accept4, sockfd, addr, addrlen, flags);
} }
static inline int compat_dup3(int oldfd, int newfd, int flags) { static inline int compat_dup3(int oldfd, int newfd, int flags) {
return syscall(__NR_dup3, oldfd, newfd, flags); return syscall(__NR_dup3, oldfd, newfd, flags);
} }
static inline ssize_t compat_readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) { static inline ssize_t compat_readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) {
return syscall(__NR_readlinkat, dirfd, pathname, buf, bufsiz); return syscall(__NR_readlinkat, dirfd, pathname, buf, bufsiz);
} }
static inline int compat_symlinkat(const char *target, int newdirfd, const char *linkpath) { static inline int compat_symlinkat(const char *target, int newdirfd, const char *linkpath) {
return syscall(__NR_symlinkat, target, newdirfd, linkpath); return syscall(__NR_symlinkat, target, newdirfd, linkpath);
} }
static inline int compat_linkat(int olddirfd, const char *oldpath, static inline int compat_linkat(int olddirfd, const char *oldpath,
int newdirfd, const char *newpath, int flags) { int newdirfd, const char *newpath, int flags) {
return syscall(__NR_linkat, olddirfd, oldpath, newdirfd, newpath, flags); return syscall(__NR_linkat, olddirfd, oldpath, newdirfd, newpath, flags);
} }
static inline int compat_inotify_init1(int flags) { static inline int compat_inotify_init1(int flags) {
return syscall(__NR_inotify_init1, flags); return syscall(__NR_inotify_init1, flags);
} }
static inline int compat_faccessat(int dirfd, const char *pathname, int mode, int flags) { static inline int compat_faccessat(int dirfd, const char *pathname, int mode, int flags) {
return syscall(__NR_faccessat, dirfd, pathname, mode, flags); return syscall(__NR_faccessat, dirfd, pathname, mode, flags);
} }

View File

@@ -12,67 +12,67 @@ static int stub(const char *) { return 0; }
static int stub(const char *, const char *) { return 0; } static int stub(const char *, const char *) { return 0; }
static int stub(const char *, char **ctx) { static int stub(const char *, char **ctx) {
*ctx = strdup(""); *ctx = strdup("");
return 0; return 0;
} }
static int stub(int, const char *) { return 0; } static int stub(int, const char *) { return 0; }
static int stub(int, char **ctx) { static int stub(int, char **ctx) {
*ctx = strdup(""); *ctx = strdup("");
return 0; return 0;
} }
// Builtin implementation // Builtin implementation
static void __freecon(char *s) { static void __freecon(char *s) {
free(s); free(s);
} }
static int __setcon(const char *ctx) { static int __setcon(const char *ctx) {
int fd = open("/proc/self/attr/current", O_WRONLY | O_CLOEXEC); int fd = open("/proc/self/attr/current", O_WRONLY | O_CLOEXEC);
if (fd < 0) if (fd < 0)
return fd; return fd;
size_t len = strlen(ctx) + 1; size_t len = strlen(ctx) + 1;
int rc = write(fd, ctx, len); int rc = write(fd, ctx, len);
close(fd); close(fd);
return rc != len; return rc != len;
} }
static int __getfilecon(const char *path, char **ctx) { static int __getfilecon(const char *path, char **ctx) {
char buf[1024]; char buf[1024];
int rc = syscall(__NR_getxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1); int rc = syscall(__NR_getxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1);
if (rc >= 0) if (rc >= 0)
*ctx = strdup(buf); *ctx = strdup(buf);
return rc; return rc;
} }
static int __lgetfilecon(const char *path, char **ctx) { static int __lgetfilecon(const char *path, char **ctx) {
char buf[1024]; char buf[1024];
int rc = syscall(__NR_lgetxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1); int rc = syscall(__NR_lgetxattr, path, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1);
if (rc >= 0) if (rc >= 0)
*ctx = strdup(buf); *ctx = strdup(buf);
return rc; return rc;
} }
static int __fgetfilecon(int fd, char **ctx) { static int __fgetfilecon(int fd, char **ctx) {
char buf[1024]; char buf[1024];
int rc = syscall(__NR_fgetxattr, fd, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1); int rc = syscall(__NR_fgetxattr, fd, XATTR_NAME_SELINUX, buf, sizeof(buf) - 1);
if (rc >= 0) if (rc >= 0)
*ctx = strdup(buf); *ctx = strdup(buf);
return rc; return rc;
} }
static int __setfilecon(const char *path, const char *ctx) { static int __setfilecon(const char *path, const char *ctx) {
return syscall(__NR_setxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0); return syscall(__NR_setxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
} }
static int __lsetfilecon(const char *path, const char *ctx) { static int __lsetfilecon(const char *path, const char *ctx) {
return syscall(__NR_lsetxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0); return syscall(__NR_lsetxattr, path, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
} }
static int __fsetfilecon(int fd, const char *ctx) { static int __fsetfilecon(int fd, const char *ctx) {
return syscall(__NR_fsetxattr, fd, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0); return syscall(__NR_fsetxattr, fd, XATTR_NAME_SELINUX, ctx, strlen(ctx) + 1, 0);
} }
// Function pointers // Function pointers
@@ -87,34 +87,34 @@ int (*lsetfilecon)(const char *, const char *) = stub;
int (*fsetfilecon)(int, const char *) = stub; int (*fsetfilecon)(int, const char *) = stub;
void getfilecon_at(int dirfd, const char *name, char **con) { void getfilecon_at(int dirfd, const char *name, char **con) {
char path[4096]; char path[4096];
fd_pathat(dirfd, name, path, sizeof(path)); fd_pathat(dirfd, name, path, sizeof(path));
if (lgetfilecon(path, con)) if (lgetfilecon(path, con))
*con = strdup(""); *con = strdup("");
} }
void setfilecon_at(int dirfd, const char *name, const char *con) { void setfilecon_at(int dirfd, const char *name, const char *con) {
char path[4096]; char path[4096];
fd_pathat(dirfd, name, path, sizeof(path)); fd_pathat(dirfd, name, path, sizeof(path));
lsetfilecon(path, con); lsetfilecon(path, con);
} }
void selinux_builtin_impl() { void selinux_builtin_impl() {
setcon = __setcon; setcon = __setcon;
getfilecon = __getfilecon; getfilecon = __getfilecon;
lgetfilecon = __lgetfilecon; lgetfilecon = __lgetfilecon;
fgetfilecon = __fgetfilecon; fgetfilecon = __fgetfilecon;
setfilecon = __setfilecon; setfilecon = __setfilecon;
lsetfilecon = __lsetfilecon; lsetfilecon = __lsetfilecon;
fsetfilecon = __fsetfilecon; fsetfilecon = __fsetfilecon;
} }
void dload_selinux() { void dload_selinux() {
if (access("/system/lib/libselinux.so", F_OK)) if (access("/system/lib/libselinux.so", F_OK))
return; return;
/* We only check whether libselinux.so exists but don't dlopen. /* We only check whether libselinux.so exists but don't dlopen.
* For some reason calling symbols returned from dlsym * For some reason calling symbols returned from dlsym
* will result to SEGV_ACCERR on some devices. * will result to SEGV_ACCERR on some devices.
* Always use builtin implementations for SELinux stuffs. */ * Always use builtin implementations for SELinux stuffs. */
selinux_builtin_impl(); selinux_builtin_impl();
} }

View File

@@ -2,128 +2,128 @@
#include <stream.hpp> #include <stream.hpp>
static int strm_read(void *v, char *buf, int len) { static int strm_read(void *v, char *buf, int len) {
auto strm = reinterpret_cast<stream *>(v); auto strm = reinterpret_cast<stream *>(v);
return strm->read(buf, len); return strm->read(buf, len);
} }
static int strm_write(void *v, const char *buf, int len) { static int strm_write(void *v, const char *buf, int len) {
auto strm = reinterpret_cast<stream *>(v); auto strm = reinterpret_cast<stream *>(v);
return strm->write(buf, len); return strm->write(buf, len);
} }
static fpos_t strm_seek(void *v, fpos_t off, int whence) { static fpos_t strm_seek(void *v, fpos_t off, int whence) {
auto strm = reinterpret_cast<stream *>(v); auto strm = reinterpret_cast<stream *>(v);
return strm->seek(off, whence); return strm->seek(off, whence);
} }
static int strm_close(void *v) { static int strm_close(void *v) {
auto strm = reinterpret_cast<stream *>(v); auto strm = reinterpret_cast<stream *>(v);
delete strm; delete strm;
return 0; return 0;
} }
sFILE make_stream_fp(stream_ptr &&strm) { sFILE make_stream_fp(stream_ptr &&strm) {
auto fp = make_file(funopen(strm.release(), strm_read, strm_write, strm_seek, strm_close)); auto fp = make_file(funopen(strm.release(), strm_read, strm_write, strm_seek, strm_close));
setbuf(fp.get(), nullptr); setbuf(fp.get(), nullptr);
return fp; return fp;
} }
int stream::read(void *buf, size_t len) { int stream::read(void *buf, size_t len) {
LOGE("This stream does not support read\n"); LOGE("This stream does not support read\n");
return -1; return -1;
} }
int stream::write(const void *buf, size_t len) { int stream::write(const void *buf, size_t len) {
LOGE("This stream does not support write\n"); LOGE("This stream does not support write\n");
return -1; return -1;
} }
off_t stream::seek(off_t off, int whence) { off_t stream::seek(off_t off, int whence) {
LOGE("This stream does not support seek\n"); LOGE("This stream does not support seek\n");
return -1; return -1;
} }
int fp_stream::read(void *buf, size_t len) { int fp_stream::read(void *buf, size_t len) {
return fread(buf, 1, len, fp.get()); return fread(buf, 1, len, fp.get());
} }
int fp_stream::write(const void *buf, size_t len) { int fp_stream::write(const void *buf, size_t len) {
return fwrite(buf, 1, len, fp.get()); return fwrite(buf, 1, len, fp.get());
} }
off_t fp_stream::seek(off_t off, int whence) { off_t fp_stream::seek(off_t off, int whence) {
return fseek(fp.get(), off, whence); return fseek(fp.get(), off, whence);
} }
int filter_stream::read(void *buf, size_t len) { int filter_stream::read(void *buf, size_t len) {
return base->read(buf, len); return base->read(buf, len);
} }
int filter_stream::write(const void *buf, size_t len) { int filter_stream::write(const void *buf, size_t len) {
return base->write(buf, len); return base->write(buf, len);
} }
byte_stream::byte_stream(uint8_t *&buf, size_t &len) : _buf(buf), _len(len) { byte_stream::byte_stream(uint8_t *&buf, size_t &len) : _buf(buf), _len(len) {
buf = nullptr; buf = nullptr;
len = 0; len = 0;
} }
int byte_stream::read(void *buf, size_t len) { int byte_stream::read(void *buf, size_t len) {
len = std::min(len, _len - _pos); len = std::min(len, _len - _pos);
memcpy(buf, _buf + _pos, len); memcpy(buf, _buf + _pos, len);
return len; return len;
} }
int byte_stream::write(const void *buf, size_t len) { int byte_stream::write(const void *buf, size_t len) {
resize(_pos + len); resize(_pos + len);
memcpy(_buf + _pos, buf, len); memcpy(_buf + _pos, buf, len);
_pos += len; _pos += len;
_len = std::max(_len, _pos); _len = std::max(_len, _pos);
return len; return len;
} }
off_t byte_stream::seek(off_t off, int whence) { off_t byte_stream::seek(off_t off, int whence) {
off_t np; off_t np;
switch (whence) { switch (whence) {
case SEEK_CUR: case SEEK_CUR:
np = _pos + off; np = _pos + off;
break; break;
case SEEK_END: case SEEK_END:
np = _len + off; np = _len + off;
break; break;
case SEEK_SET: case SEEK_SET:
np = off; np = off;
break; break;
default: default:
return -1; return -1;
} }
resize(np, true); resize(np, true);
_pos = np; _pos = np;
return np; return np;
} }
void byte_stream::resize(size_t new_pos, bool zero) { void byte_stream::resize(size_t new_pos, bool zero) {
bool resize = false; bool resize = false;
size_t old_cap = _cap; size_t old_cap = _cap;
while (new_pos > _cap) { while (new_pos > _cap) {
_cap = _cap ? (_cap << 1) - (_cap >> 1) : 1 << 12; _cap = _cap ? (_cap << 1) - (_cap >> 1) : 1 << 12;
resize = true; resize = true;
} }
if (resize) { if (resize) {
_buf = (uint8_t *) xrealloc(_buf, _cap); _buf = (uint8_t *) xrealloc(_buf, _cap);
if (zero) if (zero)
memset(_buf + old_cap, 0, _cap - old_cap); memset(_buf + old_cap, 0, _cap - old_cap);
} }
} }
int fd_stream::read(void *buf, size_t len) { int fd_stream::read(void *buf, size_t len) {
return ::read(fd, buf, len); return ::read(fd, buf, len);
} }
int fd_stream::write(const void *buf, size_t len) { int fd_stream::write(const void *buf, size_t len) {
return ::write(fd, buf, len); return ::write(fd, buf, len);
} }
off_t fd_stream::seek(off_t off, int whence) { off_t fd_stream::seek(off_t off, int whence) {
return lseek(fd, off, whence); return lseek(fd, off, whence);
} }

View File

@@ -15,458 +15,458 @@
using namespace std; using namespace std;
FILE *xfopen(const char *pathname, const char *mode) { FILE *xfopen(const char *pathname, const char *mode) {
FILE *fp = fopen(pathname, mode); FILE *fp = fopen(pathname, mode);
if (fp == nullptr) { if (fp == nullptr) {
PLOGE("fopen: %s", pathname); PLOGE("fopen: %s", pathname);
} }
return fp; return fp;
} }
FILE *xfdopen(int fd, const char *mode) { FILE *xfdopen(int fd, const char *mode) {
FILE *fp = fdopen(fd, mode); FILE *fp = fdopen(fd, mode);
if (fp == nullptr) { if (fp == nullptr) {
PLOGE("fopen"); PLOGE("fopen");
} }
return fp; return fp;
} }
int xopen(const char *pathname, int flags) { int xopen(const char *pathname, int flags) {
int fd = open(pathname, flags); int fd = open(pathname, flags);
if (fd < 0) { if (fd < 0) {
PLOGE("open: %s", pathname); PLOGE("open: %s", pathname);
} }
return fd; return fd;
} }
int xopen(const char *pathname, int flags, mode_t mode) { int xopen(const char *pathname, int flags, mode_t mode) {
int fd = open(pathname, flags, mode); int fd = open(pathname, flags, mode);
if (fd < 0) { if (fd < 0) {
PLOGE("open: %s", pathname); PLOGE("open: %s", pathname);
} }
return fd; return fd;
} }
int xopenat(int dirfd, const char *pathname, int flags) { int xopenat(int dirfd, const char *pathname, int flags) {
int fd = openat(dirfd, pathname, flags); int fd = openat(dirfd, pathname, flags);
if (fd < 0) { if (fd < 0) {
PLOGE("openat: %s", pathname); PLOGE("openat: %s", pathname);
} }
return fd; return fd;
} }
int xopenat(int dirfd, const char *pathname, int flags, mode_t mode) { int xopenat(int dirfd, const char *pathname, int flags, mode_t mode) {
int fd = openat(dirfd, pathname, flags, mode); int fd = openat(dirfd, pathname, flags, mode);
if (fd < 0) { if (fd < 0) {
PLOGE("openat: %s", pathname); PLOGE("openat: %s", pathname);
} }
return fd; return fd;
} }
ssize_t xwrite(int fd, const void *buf, size_t count) { ssize_t xwrite(int fd, const void *buf, size_t count) {
int ret = write(fd, buf, count); int ret = write(fd, buf, count);
if (count != ret) { if (count != ret) {
PLOGE("write"); PLOGE("write");
} }
return ret; return ret;
} }
// Read error other than EOF // Read error other than EOF
ssize_t xread(int fd, void *buf, size_t count) { ssize_t xread(int fd, void *buf, size_t count) {
int ret = read(fd, buf, count); int ret = read(fd, buf, count);
if (ret < 0) { if (ret < 0) {
PLOGE("read"); PLOGE("read");
} }
return ret; return ret;
} }
// Read exact same size as count // Read exact same size as count
ssize_t xxread(int fd, void *buf, size_t count) { ssize_t xxread(int fd, void *buf, size_t count) {
int ret = read(fd, buf, count); int ret = read(fd, buf, count);
if (count != ret) { if (count != ret) {
PLOGE("read (%zu != %d)", count, ret); PLOGE("read (%zu != %d)", count, ret);
} }
return ret; return ret;
} }
int xpipe2(int pipefd[2], int flags) { int xpipe2(int pipefd[2], int flags) {
int ret = pipe2(pipefd, flags); int ret = pipe2(pipefd, flags);
if (ret < 0) { if (ret < 0) {
PLOGE("pipe2"); PLOGE("pipe2");
} }
return ret; return ret;
} }
int xsetns(int fd, int nstype) { int xsetns(int fd, int nstype) {
int ret = setns(fd, nstype); int ret = setns(fd, nstype);
if (ret < 0) { if (ret < 0) {
PLOGE("setns"); PLOGE("setns");
} }
return ret; return ret;
} }
int xunshare(int flags) { int xunshare(int flags) {
int ret = unshare(flags); int ret = unshare(flags);
if (ret < 0) { if (ret < 0) {
PLOGE("unshare"); PLOGE("unshare");
} }
return ret; return ret;
} }
DIR *xopendir(const char *name) { DIR *xopendir(const char *name) {
DIR *d = opendir(name); DIR *d = opendir(name);
if (d == nullptr) { if (d == nullptr) {
PLOGE("opendir: %s", name); PLOGE("opendir: %s", name);
} }
return d; return d;
} }
DIR *xfdopendir(int fd) { DIR *xfdopendir(int fd) {
DIR *d = fdopendir(fd); DIR *d = fdopendir(fd);
if (d == nullptr) { if (d == nullptr) {
PLOGE("fdopendir"); PLOGE("fdopendir");
} }
return d; return d;
} }
struct dirent *xreaddir(DIR *dirp) { struct dirent *xreaddir(DIR *dirp) {
errno = 0; errno = 0;
for (dirent *e;;) { for (dirent *e;;) {
e = readdir(dirp); e = readdir(dirp);
if (e == nullptr) { if (e == nullptr) {
if (errno) if (errno)
PLOGE("readdir"); PLOGE("readdir");
return nullptr; return nullptr;
} else if (e->d_name == "."sv || e->d_name == ".."sv) { } else if (e->d_name == "."sv || e->d_name == ".."sv) {
// Filter . and .. for users // Filter . and .. for users
continue; continue;
} }
return e; return e;
} }
} }
pid_t xsetsid() { pid_t xsetsid() {
pid_t pid = setsid(); pid_t pid = setsid();
if (pid < 0) { if (pid < 0) {
PLOGE("setsid"); PLOGE("setsid");
} }
return pid; return pid;
} }
int xsocket(int domain, int type, int protocol) { int xsocket(int domain, int type, int protocol) {
int fd = socket(domain, type, protocol); int fd = socket(domain, type, protocol);
if (fd < 0) { if (fd < 0) {
PLOGE("socket"); PLOGE("socket");
} }
return fd; return fd;
} }
int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
int ret = bind(sockfd, addr, addrlen); int ret = bind(sockfd, addr, addrlen);
if (ret < 0) { if (ret < 0) {
PLOGE("bind"); PLOGE("bind");
} }
return ret; return ret;
} }
int xlisten(int sockfd, int backlog) { int xlisten(int sockfd, int backlog) {
int ret = listen(sockfd, backlog); int ret = listen(sockfd, backlog);
if (ret < 0) { if (ret < 0) {
PLOGE("listen"); PLOGE("listen");
} }
return ret; return ret;
} }
static int accept4_compat(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) { static int accept4_compat(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) {
int fd = accept(sockfd, addr, addrlen); int fd = accept(sockfd, addr, addrlen);
if (fd < 0) { if (fd < 0) {
PLOGE("accept"); PLOGE("accept");
} else { } else {
if (flags & SOCK_CLOEXEC) if (flags & SOCK_CLOEXEC)
fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd, F_SETFD, FD_CLOEXEC);
if (flags & SOCK_NONBLOCK) { if (flags & SOCK_NONBLOCK) {
int i = fcntl(fd, F_GETFL); int i = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, i | O_NONBLOCK); fcntl(fd, F_SETFL, i | O_NONBLOCK);
} }
} }
return fd; return fd;
} }
int xaccept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) { int xaccept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) {
int fd = accept4(sockfd, addr, addrlen, flags); int fd = accept4(sockfd, addr, addrlen, flags);
if (fd < 0) { if (fd < 0) {
if (errno == ENOSYS) if (errno == ENOSYS)
return accept4_compat(sockfd, addr, addrlen, flags); return accept4_compat(sockfd, addr, addrlen, flags);
PLOGE("accept4"); PLOGE("accept4");
} }
return fd; return fd;
} }
void *xmalloc(size_t size) { void *xmalloc(size_t size) {
void *p = malloc(size); void *p = malloc(size);
if (p == nullptr) { if (p == nullptr) {
PLOGE("malloc"); PLOGE("malloc");
} }
return p; return p;
} }
void *xcalloc(size_t nmemb, size_t size) { void *xcalloc(size_t nmemb, size_t size) {
void *p = calloc(nmemb, size); void *p = calloc(nmemb, size);
if (p == nullptr) { if (p == nullptr) {
PLOGE("calloc"); PLOGE("calloc");
} }
return p; return p;
} }
void *xrealloc(void *ptr, size_t size) { void *xrealloc(void *ptr, size_t size) {
void *p = realloc(ptr, size); void *p = realloc(ptr, size);
if (p == nullptr) { if (p == nullptr) {
PLOGE("realloc"); PLOGE("realloc");
} }
return p; return p;
} }
ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags) { ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags) {
int sent = sendmsg(sockfd, msg, flags); int sent = sendmsg(sockfd, msg, flags);
if (sent < 0) { if (sent < 0) {
PLOGE("sendmsg"); PLOGE("sendmsg");
} }
return sent; return sent;
} }
ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) { ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) {
int rec = recvmsg(sockfd, msg, flags); int rec = recvmsg(sockfd, msg, flags);
if (rec < 0) { if (rec < 0) {
PLOGE("recvmsg"); PLOGE("recvmsg");
} }
return rec; return rec;
} }
int xpthread_create(pthread_t *thread, const pthread_attr_t *attr, int xpthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg) { void *(*start_routine) (void *), void *arg) {
errno = pthread_create(thread, attr, start_routine, arg); errno = pthread_create(thread, attr, start_routine, arg);
if (errno) { if (errno) {
PLOGE("pthread_create"); PLOGE("pthread_create");
} }
return errno; return errno;
} }
int xstat(const char *pathname, struct stat *buf) { int xstat(const char *pathname, struct stat *buf) {
int ret = stat(pathname, buf); int ret = stat(pathname, buf);
if (ret < 0) { if (ret < 0) {
PLOGE("stat %s", pathname); PLOGE("stat %s", pathname);
} }
return ret; return ret;
} }
int xlstat(const char *pathname, struct stat *buf) { int xlstat(const char *pathname, struct stat *buf) {
int ret = lstat(pathname, buf); int ret = lstat(pathname, buf);
if (ret < 0) { if (ret < 0) {
PLOGE("lstat %s", pathname); PLOGE("lstat %s", pathname);
} }
return ret; return ret;
} }
int xfstat(int fd, struct stat *buf) { int xfstat(int fd, struct stat *buf) {
int ret = fstat(fd, buf); int ret = fstat(fd, buf);
if (ret < 0) { if (ret < 0) {
PLOGE("fstat %d", fd); PLOGE("fstat %d", fd);
} }
return ret; return ret;
} }
int xdup(int fd) { int xdup(int fd) {
int ret = dup(fd); int ret = dup(fd);
if (ret < 0) { if (ret < 0) {
PLOGE("dup"); PLOGE("dup");
} }
return ret; return ret;
} }
int xdup2(int oldfd, int newfd) { int xdup2(int oldfd, int newfd) {
int ret = dup2(oldfd, newfd); int ret = dup2(oldfd, newfd);
if (ret < 0) { if (ret < 0) {
PLOGE("dup2"); PLOGE("dup2");
} }
return ret; return ret;
} }
int xdup3(int oldfd, int newfd, int flags) { int xdup3(int oldfd, int newfd, int flags) {
int ret = dup3(oldfd, newfd, flags); int ret = dup3(oldfd, newfd, flags);
if (ret < 0) { if (ret < 0) {
PLOGE("dup3"); PLOGE("dup3");
} }
return ret; return ret;
} }
ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz) { ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz) {
ssize_t ret = readlink(pathname, buf, bufsiz); ssize_t ret = readlink(pathname, buf, bufsiz);
if (ret < 0) { if (ret < 0) {
PLOGE("readlink %s", pathname); PLOGE("readlink %s", pathname);
} else { } else {
buf[ret] = '\0'; buf[ret] = '\0';
} }
return ret; return ret;
} }
ssize_t xreadlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) { ssize_t xreadlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) {
// readlinkat() may fail on x86 platform, returning random value // readlinkat() may fail on x86 platform, returning random value
// instead of number of bytes placed in buf (length of link) // instead of number of bytes placed in buf (length of link)
#if defined(__i386__) || defined(__x86_64__) #if defined(__i386__) || defined(__x86_64__)
memset(buf, 0, bufsiz); memset(buf, 0, bufsiz);
ssize_t ret = readlinkat(dirfd, pathname, buf, bufsiz); ssize_t ret = readlinkat(dirfd, pathname, buf, bufsiz);
if (ret < 0) { if (ret < 0) {
PLOGE("readlinkat %s", pathname); PLOGE("readlinkat %s", pathname);
} }
return ret; return ret;
#else #else
ssize_t ret = readlinkat(dirfd, pathname, buf, bufsiz); ssize_t ret = readlinkat(dirfd, pathname, buf, bufsiz);
if (ret < 0) { if (ret < 0) {
PLOGE("readlinkat %s", pathname); PLOGE("readlinkat %s", pathname);
} else { } else {
buf[ret] = '\0'; buf[ret] = '\0';
} }
return ret; return ret;
#endif #endif
} }
int xsymlink(const char *target, const char *linkpath) { int xsymlink(const char *target, const char *linkpath) {
int ret = symlink(target, linkpath); int ret = symlink(target, linkpath);
if (ret < 0) { if (ret < 0) {
PLOGE("symlink %s->%s", target, linkpath); PLOGE("symlink %s->%s", target, linkpath);
} }
return ret; return ret;
} }
int xsymlinkat(const char *target, int newdirfd, const char *linkpath) { int xsymlinkat(const char *target, int newdirfd, const char *linkpath) {
int ret = symlinkat(target, newdirfd, linkpath); int ret = symlinkat(target, newdirfd, linkpath);
if (ret < 0) { if (ret < 0) {
PLOGE("symlinkat %s->%s", target, linkpath); PLOGE("symlinkat %s->%s", target, linkpath);
} }
return ret; return ret;
} }
int xlinkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags) { int xlinkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags) {
int ret = linkat(olddirfd, oldpath, newdirfd, newpath, flags); int ret = linkat(olddirfd, oldpath, newdirfd, newpath, flags);
if (ret < 0) { if (ret < 0) {
PLOGE("linkat %s->%s", oldpath, newpath); PLOGE("linkat %s->%s", oldpath, newpath);
} }
return ret; return ret;
} }
int xmount(const char *source, const char *target, int xmount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags, const char *filesystemtype, unsigned long mountflags,
const void *data) { const void *data) {
int ret = mount(source, target, filesystemtype, mountflags, data); int ret = mount(source, target, filesystemtype, mountflags, data);
if (ret < 0) { if (ret < 0) {
PLOGE("mount %s->%s", source, target); PLOGE("mount %s->%s", source, target);
} }
return ret; return ret;
} }
int xumount(const char *target) { int xumount(const char *target) {
int ret = umount(target); int ret = umount(target);
if (ret < 0) { if (ret < 0) {
PLOGE("umount %s", target); PLOGE("umount %s", target);
} }
return ret; return ret;
} }
int xumount2(const char *target, int flags) { int xumount2(const char *target, int flags) {
int ret = umount2(target, flags); int ret = umount2(target, flags);
if (ret < 0) { if (ret < 0) {
PLOGE("umount2 %s", target); PLOGE("umount2 %s", target);
} }
return ret; return ret;
} }
int xrename(const char *oldpath, const char *newpath) { int xrename(const char *oldpath, const char *newpath) {
int ret = rename(oldpath, newpath); int ret = rename(oldpath, newpath);
if (ret < 0) { if (ret < 0) {
PLOGE("rename %s->%s", oldpath, newpath); PLOGE("rename %s->%s", oldpath, newpath);
} }
return ret; return ret;
} }
int xmkdir(const char *pathname, mode_t mode) { int xmkdir(const char *pathname, mode_t mode) {
int ret = mkdir(pathname, mode); int ret = mkdir(pathname, mode);
if (ret < 0 && errno != EEXIST) { if (ret < 0 && errno != EEXIST) {
PLOGE("mkdir %s %u", pathname, mode); PLOGE("mkdir %s %u", pathname, mode);
} }
return ret; return ret;
} }
int xmkdirs(const char *pathname, mode_t mode) { int xmkdirs(const char *pathname, mode_t mode) {
int ret = mkdirs(pathname, mode); int ret = mkdirs(pathname, mode);
if (ret < 0) { if (ret < 0) {
PLOGE("mkdirs %s", pathname); PLOGE("mkdirs %s", pathname);
} }
return ret; return ret;
} }
int xmkdirat(int dirfd, const char *pathname, mode_t mode) { int xmkdirat(int dirfd, const char *pathname, mode_t mode) {
int ret = mkdirat(dirfd, pathname, mode); int ret = mkdirat(dirfd, pathname, mode);
if (ret < 0 && errno != EEXIST) { if (ret < 0 && errno != EEXIST) {
PLOGE("mkdirat %s %u", pathname, mode); PLOGE("mkdirat %s %u", pathname, mode);
} }
return ret; return ret;
} }
void *xmmap(void *addr, size_t length, int prot, int flags, void *xmmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset) { int fd, off_t offset) {
void *ret = mmap(addr, length, prot, flags, fd, offset); void *ret = mmap(addr, length, prot, flags, fd, offset);
if (ret == MAP_FAILED) { if (ret == MAP_FAILED) {
PLOGE("mmap"); PLOGE("mmap");
} }
return ret; return ret;
} }
ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count) { 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); ssize_t ret = sendfile(out_fd, in_fd, offset, count);
if (count != ret) { if (count != ret) {
PLOGE("sendfile"); PLOGE("sendfile");
} }
return ret; return ret;
} }
pid_t xfork() { pid_t xfork() {
int ret = fork(); int ret = fork();
if (ret < 0) { if (ret < 0) {
PLOGE("fork"); PLOGE("fork");
} }
return ret; return ret;
} }
int xpoll(struct pollfd *fds, nfds_t nfds, int timeout) { int xpoll(struct pollfd *fds, nfds_t nfds, int timeout) {
int ret = poll(fds, nfds, timeout); int ret = poll(fds, nfds, timeout);
if (ret < 0) { if (ret < 0) {
PLOGE("poll"); PLOGE("poll");
} }
return ret; return ret;
} }
int xinotify_init1(int flags) { int xinotify_init1(int flags) {
int ret = inotify_init1(flags); int ret = inotify_init1(flags);
if (ret < 0) { if (ret < 0) {
PLOGE("inotify_init1"); PLOGE("inotify_init1");
} }
return ret; return ret;
} }
char *xrealpath(const char *path, char *resolved_path) { char *xrealpath(const char *path, char *resolved_path) {
char buf[PATH_MAX]; char buf[PATH_MAX];
char *ret = realpath(path, buf); char *ret = realpath(path, buf);
if (ret == nullptr) { if (ret == nullptr) {
PLOGE("xrealpath"); PLOGE("xrealpath");
} else { } else {
strcpy(resolved_path, buf); strcpy(resolved_path, buf);
} }
return ret; return ret;
} }
int xmknod(const char *pathname, mode_t mode, dev_t dev) { int xmknod(const char *pathname, mode_t mode, dev_t dev) {
int ret = mknod(pathname, mode, dev); int ret = mknod(pathname, mode, dev);
if (ret < 0) { if (ret < 0) {
PLOGE("mknod"); PLOGE("mknod");
} }
return ret; return ret;
} }

View File

@@ -32,7 +32,7 @@ extern "C" void *xrealloc(void *ptr, size_t size);
ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags); ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t xrecvmsg(int sockfd, 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, int xpthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg); void *(*start_routine) (void *), void *arg);
int xstat(const char *pathname, struct stat *buf); int xstat(const char *pathname, struct stat *buf);
int xlstat(const char *pathname, struct stat *buf); int xlstat(const char *pathname, struct stat *buf);
int xfstat(int fd, struct stat *buf); int xfstat(int fd, struct stat *buf);
@@ -45,8 +45,8 @@ int xsymlink(const char *target, const char *linkpath);
int xsymlinkat(const char *target, int newdirfd, 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 xlinkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags);
int xmount(const char *source, const char *target, int xmount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags, const char *filesystemtype, unsigned long mountflags,
const void *data); const void *data);
int xumount(const char *target); int xumount(const char *target);
int xumount2(const char *target, int flags); int xumount2(const char *target, int flags);
int xrename(const char *oldpath, const char *newpath); int xrename(const char *oldpath, const char *newpath);