mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-01-12 05:23:36 +00:00
Bind SQLite to Rust
This commit is contained in:
parent
8e1a44e7eb
commit
24650eefe4
@ -20,7 +20,6 @@ LOCAL_SRC_FILES := \
|
||||
core/daemon.cpp \
|
||||
core/bootstages.cpp \
|
||||
core/socket.cpp \
|
||||
core/db.cpp \
|
||||
core/package.cpp \
|
||||
core/scripting.cpp \
|
||||
core/selinux.cpp \
|
||||
|
1
native/src/Cargo.lock
generated
1
native/src/Cargo.lock
generated
@ -449,6 +449,7 @@ dependencies = [
|
||||
"num-traits",
|
||||
"pb-rs",
|
||||
"quick-protobuf",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -340,6 +340,11 @@ impl Utf8CStr {
|
||||
Self::from_cstr(unsafe { CStr::from_ptr(ptr) })
|
||||
}
|
||||
|
||||
pub unsafe fn from_ptr_unchecked<'a>(ptr: *const c_char) -> &'a Utf8CStr {
|
||||
let cstr = CStr::from_ptr(ptr);
|
||||
Self::from_bytes_unchecked(cstr.to_bytes_with_nul())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_bytes_with_nul(&self) -> &[u8] {
|
||||
&self.0
|
||||
|
@ -18,3 +18,4 @@ num-traits = { workspace = true }
|
||||
num-derive = { workspace = true }
|
||||
quick-protobuf = { workspace = true }
|
||||
bytemuck = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <string>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <db.hpp>
|
||||
#include <sqlite.hpp>
|
||||
#include <base.hpp>
|
||||
#include <core.hpp>
|
||||
#include <selinux.hpp>
|
||||
@ -133,17 +133,6 @@ static bool check_key_combo() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool check_safe_mode() {
|
||||
int bootloop_cnt;
|
||||
db_settings dbs;
|
||||
get_db_settings(dbs, BOOTLOOP_COUNT);
|
||||
bootloop_cnt = dbs.bootloop;
|
||||
// Increment the bootloop counter
|
||||
set_db_settings(BOOTLOOP_COUNT, bootloop_cnt + 1);
|
||||
return bootloop_cnt >= 2 || get_prop("persist.sys.safemode", true) == "1" ||
|
||||
get_prop("ro.sys.safemode") == "1" || check_key_combo();
|
||||
}
|
||||
|
||||
/***********************
|
||||
* Boot Stage Handlers *
|
||||
***********************/
|
||||
@ -155,15 +144,12 @@ bool MagiskD::post_fs_data() const noexcept {
|
||||
|
||||
preserve_stub_apk();
|
||||
|
||||
bool safe_mode = false;
|
||||
|
||||
if (access(SECURE_DIR, F_OK) != 0) {
|
||||
if (SDK_INT < 24) {
|
||||
xmkdir(SECURE_DIR, 0700);
|
||||
} else {
|
||||
LOGE(SECURE_DIR " is not present, abort\n");
|
||||
safe_mode = true;
|
||||
return safe_mode;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,28 +157,31 @@ bool MagiskD::post_fs_data() const noexcept {
|
||||
|
||||
if (!magisk_env()) {
|
||||
LOGE("* Magisk environment incomplete, abort\n");
|
||||
safe_mode = true;
|
||||
return safe_mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (check_safe_mode()) {
|
||||
// Check safe mode
|
||||
int bootloop_cnt = get_db_setting(DbEntryKey::BootloopCount);
|
||||
// Increment the boot counter
|
||||
set_db_setting(DbEntryKey::BootloopCount, bootloop_cnt + 1);
|
||||
bool safe_mode = bootloop_cnt >= 2 || get_prop("persist.sys.safemode", true) == "1" ||
|
||||
get_prop("ro.sys.safemode") == "1" || check_key_combo();
|
||||
|
||||
if (safe_mode) {
|
||||
LOGI("* Safe mode triggered\n");
|
||||
safe_mode = true;
|
||||
// Disable all modules and zygisk so next boot will be clean
|
||||
disable_modules();
|
||||
set_db_settings(ZYGISK_CONFIG, false);
|
||||
return safe_mode;
|
||||
set_db_setting(DbEntryKey::ZygiskConfig, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
exec_common_scripts("post-fs-data");
|
||||
db_settings dbs;
|
||||
get_db_settings(dbs, ZYGISK_CONFIG);
|
||||
zygisk_enabled = dbs.zygisk;
|
||||
zygisk_enabled = get_db_setting(DbEntryKey::ZygiskConfig);
|
||||
initialize_denylist();
|
||||
setup_mounts();
|
||||
handle_modules();
|
||||
load_modules();
|
||||
return safe_mode;
|
||||
return false;
|
||||
}
|
||||
|
||||
void MagiskD::late_start() const noexcept {
|
||||
@ -210,7 +199,7 @@ void MagiskD::boot_complete() const noexcept {
|
||||
LOGI("** boot-complete triggered\n");
|
||||
|
||||
// Reset the bootloop counter once we have boot-complete
|
||||
set_db_settings(BOOTLOOP_COUNT, 0);
|
||||
set_db_setting(DbEntryKey::BootloopCount, 0);
|
||||
|
||||
// At this point it's safe to create the folder
|
||||
if (access(SECURE_DIR, F_OK) != 0)
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <base.hpp>
|
||||
#include <core.hpp>
|
||||
#include <selinux.hpp>
|
||||
#include <db.hpp>
|
||||
#include <sqlite.hpp>
|
||||
#include <flags.h>
|
||||
|
||||
using namespace std;
|
||||
@ -151,7 +151,7 @@ static void handle_request_async(int client, int code, const sock_cred &cred) {
|
||||
close(client);
|
||||
break;
|
||||
case +RequestCode::SQLITE_CMD:
|
||||
exec_sql(client);
|
||||
MagiskD().db_exec(client);
|
||||
break;
|
||||
case +RequestCode::REMOVE_MODULES: {
|
||||
int do_reboot = read_int(client);
|
||||
|
@ -1,15 +1,16 @@
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::BufReader;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
use base::libc::{O_CLOEXEC, O_RDONLY};
|
||||
use base::{
|
||||
cstr, libc, open_fd, BufReadExt, Directory, FsPathBuf, ResultExt, Utf8CStr, Utf8CStrBuf,
|
||||
Utf8CStrBufArr, Utf8CStrBufRef, WalkResult,
|
||||
cstr, libc, open_fd, BufReadExt, Directory, FsPathBuf, ReadExt, ResultExt, Utf8CStr,
|
||||
Utf8CStrBuf, Utf8CStrBufArr, Utf8CStrBufRef, WalkResult,
|
||||
};
|
||||
use bytemuck::bytes_of;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{BufReader, Read, Write};
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
use crate::consts::MAIN_CONFIG;
|
||||
use crate::db::Sqlite3;
|
||||
use crate::ffi::{get_magisk_tmp, RequestCode};
|
||||
use crate::get_prop;
|
||||
use crate::logging::magisk_logging;
|
||||
@ -42,6 +43,7 @@ impl BootStateFlags {
|
||||
#[derive(Default)]
|
||||
pub struct MagiskD {
|
||||
pub logd: Mutex<Option<File>>,
|
||||
pub sql_connection: Mutex<Option<Sqlite3>>,
|
||||
boot_stage_lock: Mutex<BootStateFlags>,
|
||||
is_emulator: bool,
|
||||
is_recovery: bool,
|
||||
@ -187,3 +189,39 @@ pub fn find_apk_path(pkg: &Utf8CStr, data: &mut [u8]) -> usize {
|
||||
.log()
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
pub trait IpcRead {
|
||||
fn ipc_read_int(&mut self) -> io::Result<i32>;
|
||||
fn ipc_read_string(&mut self) -> io::Result<String>;
|
||||
}
|
||||
|
||||
impl<T: Read> IpcRead for T {
|
||||
fn ipc_read_int(&mut self) -> io::Result<i32> {
|
||||
let mut val: i32 = 0;
|
||||
self.read_pod(&mut val)?;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn ipc_read_string(&mut self) -> io::Result<String> {
|
||||
let len = self.ipc_read_int()?;
|
||||
let mut val = "".to_string();
|
||||
self.take(len as u64).read_to_string(&mut val)?;
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IpcWrite {
|
||||
fn ipc_write_int(&mut self, val: i32) -> io::Result<()>;
|
||||
fn ipc_write_string(&mut self, val: &str) -> io::Result<()>;
|
||||
}
|
||||
|
||||
impl<T: Write> IpcWrite for T {
|
||||
fn ipc_write_int(&mut self, val: i32) -> io::Result<()> {
|
||||
self.write_all(bytes_of(&val))
|
||||
}
|
||||
|
||||
fn ipc_write_string(&mut self, val: &str) -> io::Result<()> {
|
||||
self.ipc_write_int(val.len() as i32)?;
|
||||
self.write_all(val.as_bytes())
|
||||
}
|
||||
}
|
||||
|
@ -1,300 +0,0 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
#include <db.hpp>
|
||||
#include <sqlite.hpp>
|
||||
#include <core.hpp>
|
||||
|
||||
#define DB_VERSION 12
|
||||
#define DB_VERSION_STR "12"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define DBLOGV(...)
|
||||
//#define DBLOGV(...) LOGD("magiskdb: " __VA_ARGS__)
|
||||
|
||||
#define sql_chk_log(fn, ...) if (int rc = fn(__VA_ARGS__); rc != SQLITE_OK) { \
|
||||
LOGE("sqlite3(db.cpp:%d): %s\n", __LINE__, sqlite3_errstr(rc)); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
static bool open_and_init_db_impl(sqlite3 **dbOut) {
|
||||
if (!load_sqlite()) {
|
||||
LOGE("sqlite3: Cannot load libsqlite.so\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
unique_ptr<sqlite3, decltype(sqlite3_close)> db(nullptr, sqlite3_close);
|
||||
{
|
||||
sqlite3 *sql;
|
||||
sql_chk_log(sqlite3_open_v2, MAGISKDB, &sql,
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, nullptr);
|
||||
db.reset(sql);
|
||||
}
|
||||
|
||||
int ver = 0;
|
||||
bool upgrade = false;
|
||||
auto ver_cb = [](void *ver, auto, DbValues &data) {
|
||||
*static_cast<int *>(ver) = data.get_int(0);
|
||||
};
|
||||
sql_chk_log(sql_exec, db.get(), "PRAGMA user_version", nullptr, nullptr, ver_cb, &ver);
|
||||
if (ver > DB_VERSION) {
|
||||
// Don't support downgrading database
|
||||
LOGE("sqlite3: Downgrading database is not supported\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto create_policy = [&] {
|
||||
return sql_exec(db.get(),
|
||||
"CREATE TABLE IF NOT EXISTS policies "
|
||||
"(uid INT, policy INT, until INT, logging INT, "
|
||||
"notification INT, PRIMARY KEY(uid))");
|
||||
};
|
||||
auto create_settings = [&] {
|
||||
return sql_exec(db.get(),
|
||||
"CREATE TABLE IF NOT EXISTS settings "
|
||||
"(key TEXT, value INT, PRIMARY KEY(key))");
|
||||
};
|
||||
auto create_strings = [&] {
|
||||
return sql_exec(db.get(),
|
||||
"CREATE TABLE IF NOT EXISTS strings "
|
||||
"(key TEXT, value TEXT, PRIMARY KEY(key))");
|
||||
};
|
||||
auto create_denylist = [&] {
|
||||
return sql_exec(db.get(),
|
||||
"CREATE TABLE IF NOT EXISTS denylist "
|
||||
"(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process))");
|
||||
};
|
||||
|
||||
// Database changelog:
|
||||
//
|
||||
// 0 - 6: DB stored in app private data. There are no longer any code in the project to
|
||||
// migrate these data, so no need to take any of these versions into consideration.
|
||||
// 7 : create table `hidelist` (process TEXT, PRIMARY KEY(process))
|
||||
// 8 : add new column (package_name TEXT) to table `hidelist`
|
||||
// 9 : rebuild table `hidelist` to change primary key (PRIMARY KEY(package_name, process))
|
||||
// 10: remove table `logs`
|
||||
// 11: remove table `hidelist` and create table `denylist` (same data structure)
|
||||
// 12: rebuild table `policies` to drop column `package_name`
|
||||
|
||||
if (/* 0, 1, 2, 3, 4, 5, 6 */ ver <= 6) {
|
||||
sql_chk_log(create_policy);
|
||||
sql_chk_log(create_settings);
|
||||
sql_chk_log(create_strings);
|
||||
sql_chk_log(create_denylist);
|
||||
|
||||
// Directly jump to latest
|
||||
ver = DB_VERSION;
|
||||
upgrade = true;
|
||||
}
|
||||
if (ver == 7) {
|
||||
sql_chk_log(sql_exec, db.get(),
|
||||
"BEGIN TRANSACTION;"
|
||||
"ALTER TABLE hidelist RENAME TO hidelist_tmp;"
|
||||
"CREATE TABLE IF NOT EXISTS hidelist "
|
||||
"(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process));"
|
||||
"INSERT INTO hidelist SELECT process as package_name, process FROM hidelist_tmp;"
|
||||
"DROP TABLE hidelist_tmp;"
|
||||
"COMMIT;");
|
||||
// Directly jump to version 9
|
||||
ver = 9;
|
||||
upgrade = true;
|
||||
}
|
||||
if (ver == 8) {
|
||||
sql_chk_log(sql_exec, db.get(),
|
||||
"BEGIN TRANSACTION;"
|
||||
"ALTER TABLE hidelist RENAME TO hidelist_tmp;"
|
||||
"CREATE TABLE IF NOT EXISTS hidelist "
|
||||
"(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process));"
|
||||
"INSERT INTO hidelist SELECT * FROM hidelist_tmp;"
|
||||
"DROP TABLE hidelist_tmp;"
|
||||
"COMMIT;");
|
||||
ver = 9;
|
||||
upgrade = true;
|
||||
}
|
||||
if (ver == 9) {
|
||||
sql_chk_log(sql_exec, db.get(), "DROP TABLE IF EXISTS logs", nullptr, nullptr);
|
||||
ver = 10;
|
||||
upgrade = true;
|
||||
}
|
||||
if (ver == 10) {
|
||||
sql_chk_log(sql_exec, db.get(),
|
||||
"DROP TABLE IF EXISTS hidelist;"
|
||||
"DELETE FROM settings WHERE key='magiskhide';");
|
||||
sql_chk_log(create_denylist);
|
||||
ver = 11;
|
||||
upgrade = true;
|
||||
}
|
||||
if (ver == 11) {
|
||||
sql_chk_log(sql_exec, db.get(),
|
||||
"BEGIN TRANSACTION;"
|
||||
"ALTER TABLE policies RENAME TO policies_tmp;"
|
||||
"CREATE TABLE IF NOT EXISTS policies "
|
||||
"(uid INT, policy INT, until INT, logging INT, "
|
||||
"notification INT, PRIMARY KEY(uid));"
|
||||
"INSERT INTO policies "
|
||||
"SELECT uid, policy, until, logging, notification FROM policies_tmp;"
|
||||
"DROP TABLE policies_tmp;"
|
||||
"COMMIT;");
|
||||
ver = 12;
|
||||
upgrade = true;
|
||||
}
|
||||
|
||||
if (upgrade) {
|
||||
// Set version
|
||||
sql_chk_log(sql_exec, db.get(), "PRAGMA user_version=" DB_VERSION_STR);
|
||||
}
|
||||
|
||||
*dbOut = db.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
sqlite3 *open_and_init_db() {
|
||||
sqlite3 *db = nullptr;
|
||||
return open_and_init_db_impl(&db) ? db : nullptr;
|
||||
}
|
||||
|
||||
static sqlite3 *get_db() {
|
||||
static sqlite3 *db = nullptr;
|
||||
if (db == nullptr) {
|
||||
db = open_and_init_db();
|
||||
if (db == nullptr) {
|
||||
// Open failed, remove and reconstruct
|
||||
unlink(MAGISKDB);
|
||||
db = open_and_init_db();
|
||||
}
|
||||
}
|
||||
return db;
|
||||
}
|
||||
|
||||
bool db_exec(const char *sql, DbArgs args, db_exec_callback exec_fn) {
|
||||
using db_bind_callback = std::function<int(int, DbStatement&)>;
|
||||
|
||||
if (sqlite3 *db = get_db()) {
|
||||
db_bind_callback bind_fn = {};
|
||||
sql_bind_callback bind_cb = nullptr;
|
||||
if (!args.empty()) {
|
||||
bind_fn = std::ref(args);
|
||||
bind_cb = [](void *v, int index, DbStatement &stmt) -> int {
|
||||
auto fn = static_cast<db_bind_callback*>(v);
|
||||
return fn->operator()(index, stmt);
|
||||
};
|
||||
}
|
||||
sql_exec_callback exec_cb = nullptr;
|
||||
if (exec_fn) {
|
||||
exec_cb = [](void *v, StringSlice columns, DbValues &data) {
|
||||
auto fn = static_cast<db_exec_callback*>(v);
|
||||
fn->operator()(columns, data);
|
||||
};
|
||||
}
|
||||
sql_chk_log(sql_exec, db, sql, bind_cb, &bind_fn, exec_cb, &exec_fn);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool get_db_settings(db_settings &cfg, int key) {
|
||||
if (key >= 0) {
|
||||
return db_exec("SELECT * FROM settings WHERE key=?", { DB_SETTING_KEYS[key] }, cfg);
|
||||
} else {
|
||||
return db_exec("SELECT * FROM settings", {}, cfg);
|
||||
}
|
||||
}
|
||||
|
||||
bool set_db_settings(int key, int value) {
|
||||
return db_exec(
|
||||
"INSERT OR REPLACE INTO settings (key,value) VALUES(?,?)",
|
||||
{ DB_SETTING_KEYS[key], value });
|
||||
}
|
||||
|
||||
bool get_db_strings(db_strings &str, int key) {
|
||||
if (key >= 0) {
|
||||
return db_exec("SELECT * FROM strings WHERE key=?", { DB_STRING_KEYS[key] }, str);
|
||||
} else {
|
||||
return db_exec("SELECT * FROM strings", {}, str);
|
||||
}
|
||||
}
|
||||
|
||||
bool rm_db_strings(int key) {
|
||||
return db_exec("DELETE FROM strings WHERE key=?", { DB_STRING_KEYS[key] });
|
||||
}
|
||||
|
||||
void exec_sql(owned_fd client) {
|
||||
string sql = read_string(client);
|
||||
db_exec(sql.data(), {}, [fd = (int) client](StringSlice columns, DbValues &data) {
|
||||
string out;
|
||||
for (int i = 0; i < columns.size(); ++i) {
|
||||
if (i != 0) out += '|';
|
||||
out += columns[i].c_str();
|
||||
out += '=';
|
||||
out += data.get_text(i);
|
||||
}
|
||||
write_string(fd, out);
|
||||
});
|
||||
write_int(client, 0);
|
||||
}
|
||||
|
||||
db_settings::db_settings() :
|
||||
root_access(ROOT_ACCESS_APPS_AND_ADB),
|
||||
multiuser_mode(MULTIUSER_MODE_OWNER_ONLY),
|
||||
mnt_ns(NAMESPACE_MODE_REQUESTER),
|
||||
bootloop(0),
|
||||
denylist(false),
|
||||
zygisk(MagiskD().is_emulator()) {}
|
||||
|
||||
void db_settings::operator()(StringSlice columns, DbValues &data) {
|
||||
string_view key;
|
||||
int val;
|
||||
for (int i = 0; i < columns.size(); ++i) {
|
||||
const auto &name = columns[i];
|
||||
if (name == "key") {
|
||||
key = data.get_text(i);
|
||||
} else if (name == "value") {
|
||||
val = data.get_int(i);
|
||||
}
|
||||
}
|
||||
if (key == DB_SETTING_KEYS[ROOT_ACCESS]) {
|
||||
root_access = val;
|
||||
} else if (key == DB_SETTING_KEYS[SU_MULTIUSER_MODE]) {
|
||||
multiuser_mode = val;
|
||||
} else if (key == DB_SETTING_KEYS[SU_MNT_NS]) {
|
||||
mnt_ns = val;
|
||||
} else if (key == DB_SETTING_KEYS[BOOTLOOP_COUNT]) {
|
||||
bootloop = val;
|
||||
} else if (key == DB_SETTING_KEYS[DENYLIST_CONFIG]) {
|
||||
denylist = val;
|
||||
} else if (key == DB_SETTING_KEYS[ZYGISK_CONFIG]) {
|
||||
zygisk = val;
|
||||
}
|
||||
}
|
||||
|
||||
void db_strings::operator()(StringSlice columns, DbValues &data) {
|
||||
string_view key;
|
||||
const char *val;
|
||||
for (int i = 0; i < columns.size(); ++i) {
|
||||
const auto &name = columns[i];
|
||||
if (name == "key") {
|
||||
key = data.get_text(i);
|
||||
} else if (name == "value") {
|
||||
val = data.get_text(i);
|
||||
}
|
||||
}
|
||||
if (key == DB_STRING_KEYS[SU_MANAGER]) {
|
||||
su_manager = val;
|
||||
}
|
||||
}
|
||||
|
||||
int DbArgs::operator()(int index, DbStatement &stmt) {
|
||||
if (curr < args.size()) {
|
||||
const auto &arg = args[curr++];
|
||||
switch (arg.type) {
|
||||
case DbArg::INT:
|
||||
return stmt.bind_int64(index, arg.int_val);
|
||||
case DbArg::TEXT:
|
||||
return stmt.bind_text(index, arg.str_val);
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
337
native/src/core/db.rs
Normal file
337
native/src/core/db.rs
Normal file
@ -0,0 +1,337 @@
|
||||
#![allow(improper_ctypes, improper_ctypes_definitions)]
|
||||
use crate::daemon::{IpcRead, IpcWrite, MagiskD, MAGISKD};
|
||||
use crate::ffi::{
|
||||
open_and_init_db, sqlite3, sqlite3_errstr, DbEntryKey, DbSettings, DbStatement, DbValues,
|
||||
MntNsMode, MultiuserMode, RootAccess,
|
||||
};
|
||||
use base::{LoggedResult, ResultExt, Utf8CStr};
|
||||
use std::ffi::c_void;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, BufWriter};
|
||||
use std::os::fd::{FromRawFd, OwnedFd, RawFd};
|
||||
use std::pin::Pin;
|
||||
use std::ptr;
|
||||
use std::ptr::NonNull;
|
||||
use thiserror::Error;
|
||||
use DbArg::{Integer, Text};
|
||||
|
||||
fn sqlite_err_str(code: i32) -> &'static Utf8CStr {
|
||||
// SAFETY: sqlite3 always returns UTF-8 strings
|
||||
unsafe { Utf8CStr::from_ptr_unchecked(sqlite3_errstr(code)) }
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Error, Debug)]
|
||||
#[error("sqlite3: {}", sqlite_err_str(self.0))]
|
||||
pub struct SqliteError(i32);
|
||||
|
||||
pub type SqliteResult = Result<(), SqliteError>;
|
||||
|
||||
trait SqliteReturn {
|
||||
fn sql_result(self) -> SqliteResult;
|
||||
}
|
||||
|
||||
impl SqliteReturn for i32 {
|
||||
fn sql_result(self) -> SqliteResult {
|
||||
if self != 0 {
|
||||
Err(SqliteError(self))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait SqlTable {
|
||||
fn on_row(&mut self, columns: &[String], data: &DbValues);
|
||||
}
|
||||
|
||||
impl<T> SqlTable for T
|
||||
where
|
||||
T: FnMut(&[String], &DbValues),
|
||||
{
|
||||
fn on_row(&mut self, columns: &[String], data: &DbValues) {
|
||||
self.call_mut((columns, data))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RootAccess {
|
||||
fn default() -> Self {
|
||||
RootAccess::AppsAndAdb
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MultiuserMode {
|
||||
fn default() -> Self {
|
||||
MultiuserMode::OwnerOnly
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MntNsMode {
|
||||
fn default() -> Self {
|
||||
MntNsMode::Requester
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_default_db_settings() -> DbSettings {
|
||||
DbSettings::default()
|
||||
}
|
||||
|
||||
impl DbEntryKey {
|
||||
fn to_str(self) -> &'static str {
|
||||
match self {
|
||||
DbEntryKey::RootAccess => "root_access",
|
||||
DbEntryKey::SuMultiuserMode => "multiuser_mode",
|
||||
DbEntryKey::SuMntNs => "mnt_ns",
|
||||
DbEntryKey::DenylistConfig => "denylist",
|
||||
DbEntryKey::ZygiskConfig => "zygisk",
|
||||
DbEntryKey::BootloopCount => "bootloop",
|
||||
DbEntryKey::SuManager => "requester",
|
||||
_ => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SqlTable for DbSettings {
|
||||
fn on_row(&mut self, columns: &[String], data: &DbValues) {
|
||||
let mut key = "";
|
||||
let mut value = 0;
|
||||
for (i, column) in columns.iter().enumerate() {
|
||||
if column == "key" {
|
||||
key = data.get_text(i as i32);
|
||||
} else if column == "value" {
|
||||
value = data.get_int(i as i32);
|
||||
}
|
||||
}
|
||||
match key {
|
||||
"root_access" => self.root_access = RootAccess { repr: value },
|
||||
"multiuser_mode" => self.multiuser_mode = MultiuserMode { repr: value },
|
||||
"mnt_ns" => self.mnt_ns = MntNsMode { repr: value },
|
||||
"denylist" => self.denylist = value != 0,
|
||||
"zygisk" => self.zygisk = value != 0,
|
||||
"bootloop" => self.boot_count = value,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Sqlite3(NonNull<sqlite3>);
|
||||
unsafe impl Send for Sqlite3 {}
|
||||
|
||||
type SqlBindCallback = Option<unsafe extern "C" fn(*mut c_void, i32, Pin<&mut DbStatement>) -> i32>;
|
||||
type SqlExecCallback = Option<unsafe extern "C" fn(*mut c_void, &[String], &DbValues)>;
|
||||
|
||||
extern "C" {
|
||||
fn sql_exec_impl(
|
||||
db: *mut sqlite3,
|
||||
sql: &str,
|
||||
bind_callback: SqlBindCallback,
|
||||
bind_cookie: *mut c_void,
|
||||
exec_callback: SqlExecCallback,
|
||||
exec_cookie: *mut c_void,
|
||||
) -> i32;
|
||||
}
|
||||
|
||||
enum DbArg<'a> {
|
||||
Text(&'a str),
|
||||
Integer(i64),
|
||||
}
|
||||
|
||||
struct DbArgs<'a> {
|
||||
args: &'a [DbArg<'a>],
|
||||
curr: usize,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn bind_arguments(v: *mut c_void, idx: i32, stmt: Pin<&mut DbStatement>) -> i32 {
|
||||
let args = &mut *(v as *mut DbArgs<'_>);
|
||||
if args.curr < args.args.len() {
|
||||
let arg = &args.args[args.curr];
|
||||
match *arg {
|
||||
Text(v) => stmt.bind_text(idx, v),
|
||||
Integer(v) => stmt.bind_int64(idx, v),
|
||||
}
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn read_db_row<T: SqlTable>(
|
||||
v: *mut c_void,
|
||||
columns: &[String],
|
||||
values: &DbValues,
|
||||
) {
|
||||
let table = &mut *(v as *mut T);
|
||||
table.on_row(columns, values);
|
||||
}
|
||||
|
||||
impl MagiskD {
|
||||
fn with_db<F: FnOnce(*mut sqlite3) -> i32>(&self, f: F) -> i32 {
|
||||
let mut db = self.sql_connection.lock().unwrap();
|
||||
if db.is_none() {
|
||||
let raw_db = open_and_init_db();
|
||||
*db = NonNull::new(raw_db).map(Sqlite3);
|
||||
}
|
||||
if let Some(ref mut db) = *db {
|
||||
f(db.0.as_ptr())
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
fn db_exec_with_output<T: SqlTable>(&self, sql: &str, args: &[DbArg], out: &mut T) -> i32 {
|
||||
let mut bind_callback: SqlBindCallback = None;
|
||||
let mut bind_cookie: *mut c_void = ptr::null_mut();
|
||||
let mut db_args = DbArgs { args, curr: 0 };
|
||||
if !args.is_empty() {
|
||||
bind_callback = Some(bind_arguments);
|
||||
bind_cookie = (&mut db_args) as *mut DbArgs as *mut c_void;
|
||||
}
|
||||
let out_ptr: *mut T = out;
|
||||
|
||||
self.with_db(|db| unsafe {
|
||||
sql_exec_impl(
|
||||
db,
|
||||
sql,
|
||||
bind_callback,
|
||||
bind_cookie,
|
||||
Some(read_db_row::<T>),
|
||||
out_ptr.cast(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn db_exec(&self, sql: &str, args: &[DbArg]) -> i32 {
|
||||
let mut bind_callback: SqlBindCallback = None;
|
||||
let mut bind_cookie: *mut c_void = ptr::null_mut();
|
||||
let mut db_args = DbArgs { args, curr: 0 };
|
||||
if !args.is_empty() {
|
||||
bind_callback = Some(bind_arguments);
|
||||
bind_cookie = (&mut db_args) as *mut DbArgs as *mut c_void;
|
||||
}
|
||||
|
||||
self.with_db(|db| unsafe {
|
||||
sql_exec_impl(db, sql, bind_callback, bind_cookie, None, ptr::null_mut())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_db_setting(&self, key: DbEntryKey, value: i32) -> SqliteResult {
|
||||
self.db_exec(
|
||||
"INSERT OR REPLACE INTO settings (key,value) VALUES(?,?)",
|
||||
&[Text(key.to_str()), Integer(value as i64)],
|
||||
)
|
||||
.sql_result()
|
||||
}
|
||||
|
||||
pub fn get_db_setting(&self, key: DbEntryKey) -> i32 {
|
||||
// Get default values
|
||||
let mut val = match key {
|
||||
DbEntryKey::RootAccess => RootAccess::default().repr,
|
||||
DbEntryKey::SuMultiuserMode => MultiuserMode::default().repr,
|
||||
DbEntryKey::SuMntNs => MntNsMode::default().repr,
|
||||
DbEntryKey::DenylistConfig => 0,
|
||||
DbEntryKey::ZygiskConfig => self.is_emulator() as i32,
|
||||
DbEntryKey::BootloopCount => 0,
|
||||
_ => -1,
|
||||
};
|
||||
let mut func = |_: &[String], values: &DbValues| {
|
||||
val = values.get_int(0);
|
||||
};
|
||||
self.db_exec_with_output(
|
||||
"SELECT value FROM settings WHERE key=?",
|
||||
&[Text(key.to_str())],
|
||||
&mut func,
|
||||
)
|
||||
.sql_result()
|
||||
.log()
|
||||
.ok();
|
||||
val
|
||||
}
|
||||
|
||||
pub fn get_db_settings(&self, cfg: &mut DbSettings) -> SqliteResult {
|
||||
cfg.zygisk = self.is_emulator();
|
||||
self.db_exec_with_output("SELECT * FROM settings", &[], cfg)
|
||||
.sql_result()
|
||||
}
|
||||
|
||||
pub fn get_db_string(&self, key: DbEntryKey) -> String {
|
||||
let mut val = "".to_string();
|
||||
let mut func = |_: &[String], values: &DbValues| {
|
||||
val.push_str(values.get_text(0));
|
||||
};
|
||||
self.db_exec_with_output(
|
||||
"SELECT value FROM strings WHERE key=?",
|
||||
&[Text(key.to_str())],
|
||||
&mut func,
|
||||
)
|
||||
.sql_result()
|
||||
.log()
|
||||
.ok();
|
||||
val
|
||||
}
|
||||
|
||||
pub fn rm_db_string(&self, key: DbEntryKey) -> SqliteResult {
|
||||
self.db_exec("DELETE FROM strings WHERE key=?", &[Text(key.to_str())])
|
||||
.sql_result()
|
||||
}
|
||||
|
||||
fn db_exec_for_client(&self, fd: OwnedFd) -> LoggedResult<()> {
|
||||
let mut file = File::from(fd);
|
||||
let mut reader = BufReader::new(&mut file);
|
||||
let sql = reader.ipc_read_string()?;
|
||||
let mut writer = BufWriter::new(&mut file);
|
||||
let mut output_fn = |columns: &[String], values: &DbValues| {
|
||||
let mut out = "".to_string();
|
||||
for (i, column) in columns.iter().enumerate() {
|
||||
if i != 0 {
|
||||
out.push('|');
|
||||
}
|
||||
out.push_str(column);
|
||||
out.push('=');
|
||||
out.push_str(values.get_text(i as i32));
|
||||
}
|
||||
writer.ipc_write_string(&out).log().ok();
|
||||
};
|
||||
self.db_exec_with_output(&sql, &[], &mut output_fn);
|
||||
writer.ipc_write_string("").log()
|
||||
}
|
||||
}
|
||||
|
||||
impl MagiskD {
|
||||
pub fn get_db_settings_for_cxx(&self, cfg: &mut DbSettings) -> bool {
|
||||
self.get_db_settings(cfg).log().is_ok()
|
||||
}
|
||||
|
||||
pub fn set_db_setting_for_cxx(&self, key: DbEntryKey, value: i32) -> bool {
|
||||
self.set_db_setting(key, value).log().is_ok()
|
||||
}
|
||||
|
||||
pub fn rm_db_string_for_cxx(&self, key: DbEntryKey) -> bool {
|
||||
self.rm_db_string(key).log().is_ok()
|
||||
}
|
||||
|
||||
pub fn db_exec_for_cxx(&self, client_fd: RawFd) {
|
||||
// Take ownership
|
||||
let fd = unsafe { OwnedFd::from_raw_fd(client_fd) };
|
||||
self.db_exec_for_client(fd).ok();
|
||||
}
|
||||
}
|
||||
|
||||
#[export_name = "sql_exec_rs"]
|
||||
unsafe extern "C" fn sql_exec_for_cxx(
|
||||
sql: &str,
|
||||
bind_callback: SqlBindCallback,
|
||||
bind_cookie: *mut c_void,
|
||||
exec_callback: SqlExecCallback,
|
||||
exec_cookie: *mut c_void,
|
||||
) -> i32 {
|
||||
MAGISKD.get().unwrap_unchecked().with_db(|db| {
|
||||
sql_exec_impl(
|
||||
db,
|
||||
sql,
|
||||
bind_callback,
|
||||
bind_cookie,
|
||||
exec_callback,
|
||||
exec_cookie,
|
||||
)
|
||||
})
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
#include <db.hpp>
|
||||
#include <sqlite.hpp>
|
||||
#include <core.hpp>
|
||||
|
||||
#include "deny.hpp"
|
||||
@ -385,7 +385,7 @@ int enable_deny() {
|
||||
}
|
||||
}
|
||||
|
||||
set_db_settings(DENYLIST_CONFIG, true);
|
||||
MagiskD().set_db_setting(DbEntryKey::DenylistConfig, true);
|
||||
return DenyResponse::OK;
|
||||
}
|
||||
|
||||
@ -393,15 +393,13 @@ int disable_deny() {
|
||||
if (denylist_enforced.exchange(false)) {
|
||||
LOGI("* Disable DenyList\n");
|
||||
}
|
||||
set_db_settings(DENYLIST_CONFIG, false);
|
||||
MagiskD().set_db_setting(DbEntryKey::DenylistConfig, false);
|
||||
return DenyResponse::OK;
|
||||
}
|
||||
|
||||
void initialize_denylist() {
|
||||
if (!denylist_enforced) {
|
||||
db_settings dbs;
|
||||
get_db_settings(dbs, DENYLIST_CONFIG);
|
||||
if (dbs.denylist)
|
||||
if (MagiskD().get_db_setting(DbEntryKey::DenylistConfig))
|
||||
enable_deny();
|
||||
}
|
||||
}
|
||||
|
@ -1,128 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <functional>
|
||||
|
||||
#include <base.hpp>
|
||||
#include <sqlite.hpp>
|
||||
|
||||
/***************
|
||||
* DB Settings *
|
||||
***************/
|
||||
|
||||
constexpr const char *DB_SETTING_KEYS[] = {
|
||||
"root_access",
|
||||
"multiuser_mode",
|
||||
"mnt_ns",
|
||||
"denylist",
|
||||
"zygisk",
|
||||
"bootloop",
|
||||
};
|
||||
|
||||
// Settings key indices
|
||||
enum {
|
||||
ROOT_ACCESS = 0,
|
||||
SU_MULTIUSER_MODE,
|
||||
SU_MNT_NS,
|
||||
DENYLIST_CONFIG,
|
||||
ZYGISK_CONFIG,
|
||||
BOOTLOOP_COUNT,
|
||||
};
|
||||
|
||||
// Values for root_access
|
||||
enum {
|
||||
ROOT_ACCESS_DISABLED = 0,
|
||||
ROOT_ACCESS_APPS_ONLY,
|
||||
ROOT_ACCESS_ADB_ONLY,
|
||||
ROOT_ACCESS_APPS_AND_ADB
|
||||
};
|
||||
|
||||
// Values for multiuser_mode
|
||||
enum {
|
||||
MULTIUSER_MODE_OWNER_ONLY = 0,
|
||||
MULTIUSER_MODE_OWNER_MANAGED,
|
||||
MULTIUSER_MODE_USER
|
||||
};
|
||||
|
||||
// Values for mnt_ns
|
||||
enum {
|
||||
NAMESPACE_MODE_GLOBAL = 0,
|
||||
NAMESPACE_MODE_REQUESTER,
|
||||
NAMESPACE_MODE_ISOLATE
|
||||
};
|
||||
|
||||
struct db_settings {
|
||||
int root_access;
|
||||
int multiuser_mode;
|
||||
int mnt_ns;
|
||||
int bootloop;
|
||||
bool denylist;
|
||||
bool zygisk;
|
||||
|
||||
db_settings();
|
||||
void operator()(StringSlice columns, DbValues &data);
|
||||
};
|
||||
|
||||
/**************
|
||||
* DB Strings *
|
||||
**************/
|
||||
|
||||
constexpr const char *DB_STRING_KEYS[] = { "requester" };
|
||||
|
||||
// Strings keys indices
|
||||
enum {
|
||||
SU_MANAGER = 0
|
||||
};
|
||||
|
||||
struct db_strings {
|
||||
std::string su_manager;
|
||||
|
||||
void operator()(StringSlice columns, DbValues &data);
|
||||
};
|
||||
|
||||
/********************
|
||||
* Public Functions *
|
||||
********************/
|
||||
|
||||
using db_exec_callback = std::function<void(StringSlice, DbValues&)>;
|
||||
|
||||
struct DbArg {
|
||||
enum {
|
||||
INT,
|
||||
TEXT,
|
||||
} type;
|
||||
union {
|
||||
int64_t int_val;
|
||||
rust::Str str_val;
|
||||
};
|
||||
DbArg(int64_t v) : type(INT), int_val(v) {}
|
||||
DbArg(const char *v) : type(TEXT), str_val(v) {}
|
||||
};
|
||||
|
||||
struct DbArgs {
|
||||
DbArgs() : curr(0) {}
|
||||
DbArgs(std::initializer_list<DbArg> list) : args(list), curr(0) {}
|
||||
int operator()(int index, DbStatement &stmt);
|
||||
bool empty() const { return args.empty(); }
|
||||
private:
|
||||
std::vector<DbArg> args;
|
||||
size_t curr;
|
||||
};
|
||||
|
||||
bool get_db_settings(db_settings &cfg, int key = -1);
|
||||
bool set_db_settings(int key, int value);
|
||||
bool get_db_strings(db_strings &str, int key = -1);
|
||||
bool rm_db_strings(int key);
|
||||
void exec_sql(owned_fd client);
|
||||
bool db_exec(const char *sql, DbArgs args = {}, db_exec_callback exec_fn = {});
|
||||
|
||||
template<typename T>
|
||||
concept DbData = requires(T t, StringSlice s, DbValues &v) { t(s, v); };
|
||||
|
||||
template<DbData T>
|
||||
bool db_exec(const char *sql, DbArgs args, T &data) {
|
||||
return db_exec(sql, std::move(args), (db_exec_callback) std::ref(data));
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <cxx.h>
|
||||
|
||||
#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */
|
||||
@ -13,14 +15,13 @@
|
||||
struct sqlite3;
|
||||
struct sqlite3_stmt;
|
||||
|
||||
extern int (*sqlite3_open_v2)(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs);
|
||||
extern int (*sqlite3_close)(sqlite3 *db);
|
||||
extern const char *(*sqlite3_errstr)(int);
|
||||
|
||||
// Transparent wrappers of sqlite3_stmt
|
||||
struct DbValues {
|
||||
const char *get_text(int index);
|
||||
int get_int(int index);
|
||||
const char *get_text(int index) const;
|
||||
rust::Str get_str(int index) const { return get_text(index); }
|
||||
int get_int(int index) const;
|
||||
~DbValues() = delete;
|
||||
};
|
||||
struct DbStatement {
|
||||
@ -33,8 +34,43 @@ using StringSlice = rust::Slice<rust::String>;
|
||||
using sql_bind_callback = int(*)(void*, int, DbStatement&);
|
||||
using sql_exec_callback = void(*)(void*, StringSlice, DbValues&);
|
||||
|
||||
bool load_sqlite();
|
||||
sqlite3 *open_and_init_db();
|
||||
int sql_exec(sqlite3 *db, rust::Str zSql,
|
||||
sql_bind_callback bind_cb = nullptr, void *bind_cookie = nullptr,
|
||||
sql_exec_callback exec_cb = nullptr, void *exec_cookie = nullptr);
|
||||
|
||||
/************
|
||||
* C++ APIs *
|
||||
************/
|
||||
|
||||
using db_exec_callback = std::function<void(StringSlice, DbValues&)>;
|
||||
|
||||
struct DbArg {
|
||||
enum {
|
||||
INT,
|
||||
TEXT,
|
||||
} type;
|
||||
union {
|
||||
int64_t int_val;
|
||||
rust::Str str_val;
|
||||
};
|
||||
DbArg(int64_t v) : type(INT), int_val(v) {}
|
||||
DbArg(const char *v) : type(TEXT), str_val(v) {}
|
||||
};
|
||||
|
||||
struct DbArgs {
|
||||
DbArgs() : curr(0) {}
|
||||
DbArgs(std::initializer_list<DbArg> list) : args(list), curr(0) {}
|
||||
int operator()(int index, DbStatement &stmt);
|
||||
bool empty() const { return args.empty(); }
|
||||
private:
|
||||
std::vector<DbArg> args;
|
||||
size_t curr;
|
||||
};
|
||||
|
||||
bool db_exec(const char *sql, DbArgs args = {}, db_exec_callback exec_fn = {});
|
||||
|
||||
template<typename T>
|
||||
concept DbData = requires(T t, StringSlice s, DbValues &v) { t(s, v); };
|
||||
|
||||
template<DbData T>
|
||||
bool db_exec(const char *sql, DbArgs args, T &data) {
|
||||
return db_exec(sql, std::move(args), (db_exec_callback) std::ref(data));
|
||||
}
|
||||
|
@ -1,21 +1,24 @@
|
||||
#![feature(format_args_nl)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(fn_traits)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
use base::Utf8CStr;
|
||||
use cert::read_certificate;
|
||||
use daemon::{daemon_entry, find_apk_path, get_magiskd, MagiskD};
|
||||
use db::get_default_db_settings;
|
||||
use logging::{
|
||||
android_logging, magisk_logging, zygisk_close_logd, zygisk_get_logd, zygisk_logging,
|
||||
};
|
||||
use mount::{find_preinit_device, revert_unmount, setup_mounts, clean_mounts};
|
||||
use mount::{clean_mounts, find_preinit_device, revert_unmount, setup_mounts};
|
||||
use resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop};
|
||||
|
||||
mod cert;
|
||||
#[path = "../include/consts.rs"]
|
||||
mod consts;
|
||||
mod daemon;
|
||||
mod db;
|
||||
mod logging;
|
||||
mod mount;
|
||||
mod resetprop;
|
||||
@ -76,6 +79,67 @@ pub mod ffi {
|
||||
fn switch_mnt_ns(pid: i32) -> i32;
|
||||
}
|
||||
|
||||
enum DbEntryKey {
|
||||
RootAccess,
|
||||
SuMultiuserMode,
|
||||
SuMntNs,
|
||||
DenylistConfig,
|
||||
ZygiskConfig,
|
||||
BootloopCount,
|
||||
SuManager,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
enum RootAccess {
|
||||
Disabled,
|
||||
AppsOnly,
|
||||
AdbOnly,
|
||||
AppsAndAdb,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
enum MultiuserMode {
|
||||
OwnerOnly,
|
||||
OwnerManaged,
|
||||
User,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
enum MntNsMode {
|
||||
Global,
|
||||
Requester,
|
||||
Isolate,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct DbSettings {
|
||||
root_access: RootAccess,
|
||||
multiuser_mode: MultiuserMode,
|
||||
mnt_ns: MntNsMode,
|
||||
boot_count: i32,
|
||||
denylist: bool,
|
||||
zygisk: bool,
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("include/sqlite.hpp");
|
||||
|
||||
fn sqlite3_errstr(code: i32) -> *const c_char;
|
||||
|
||||
type sqlite3;
|
||||
fn open_and_init_db() -> *mut sqlite3;
|
||||
|
||||
type DbValues;
|
||||
type DbStatement;
|
||||
|
||||
fn get_int(self: &DbValues, index: i32) -> i32;
|
||||
#[cxx_name = "get_str"]
|
||||
fn get_text(self: &DbValues, index: i32) -> &str;
|
||||
|
||||
fn bind_text(self: Pin<&mut DbStatement>, index: i32, val: &str) -> i32;
|
||||
fn bind_int64(self: Pin<&mut DbStatement>, index: i32, val: i64) -> i32;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
fn rust_test_entry();
|
||||
fn android_logging();
|
||||
@ -102,10 +166,22 @@ pub mod ffi {
|
||||
extern "Rust" {
|
||||
type MagiskD;
|
||||
fn setup_logfile(self: &MagiskD);
|
||||
fn is_emulator(self: &MagiskD) -> bool;
|
||||
fn is_recovery(self: &MagiskD) -> bool;
|
||||
fn boot_stage_handler(self: &MagiskD, client: i32, code: i32);
|
||||
|
||||
#[cxx_name = "get_db_settings"]
|
||||
fn get_db_settings_for_cxx(self: &MagiskD, cfg: &mut DbSettings) -> bool;
|
||||
fn get_db_setting(&self, key: DbEntryKey) -> i32;
|
||||
#[cxx_name = "set_db_setting"]
|
||||
fn set_db_setting_for_cxx(&self, key: DbEntryKey, value: i32) -> bool;
|
||||
fn get_db_string(&self, key: DbEntryKey) -> String;
|
||||
#[cxx_name = "rm_db_string"]
|
||||
fn rm_db_string_for_cxx(&self, key: DbEntryKey) -> bool;
|
||||
#[cxx_name = "db_exec"]
|
||||
fn db_exec_for_cxx(&self, client_fd: i32);
|
||||
#[cxx_name = "DbSettings"]
|
||||
fn get_default_db_settings() -> DbSettings;
|
||||
|
||||
#[cxx_name = "MagiskD"]
|
||||
fn get_magiskd() -> &'static MagiskD;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <base.hpp>
|
||||
#include <consts.hpp>
|
||||
#include <core.hpp>
|
||||
#include <db.hpp>
|
||||
#include <sqlite.hpp>
|
||||
#include <flags.h>
|
||||
|
||||
using namespace std;
|
||||
@ -195,9 +195,7 @@ static int get_pkg_uid(int user, const char *pkg) {
|
||||
int get_manager(int user, string *pkg, bool install) {
|
||||
mutex_guard g(pkg_lock);
|
||||
|
||||
db_strings str;
|
||||
get_db_strings(str, SU_MANAGER);
|
||||
string db_pkg(std::move(str.su_manager));
|
||||
string db_pkg = (string) MagiskD().get_db_string(DbEntryKey::SuManager);
|
||||
|
||||
// If database changed, always re-check files
|
||||
if (db_pkg != repackaged_pkg) {
|
||||
@ -251,7 +249,7 @@ int get_manager(int user, string *pkg, bool install) {
|
||||
case CERT_MISMATCH:
|
||||
install = true;
|
||||
case NOT_INSTALLED:
|
||||
rm_db_strings(SU_MANAGER);
|
||||
MagiskD().rm_db_string(DbEntryKey::SuManager);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,22 @@
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <consts.hpp>
|
||||
#include <base.hpp>
|
||||
#include <sqlite.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define DB_VERSION 12
|
||||
#define DB_VERSION_STR "12"
|
||||
|
||||
#define DBLOGV(...)
|
||||
//#define DBLOGV(...) LOGD("magiskdb: " __VA_ARGS__)
|
||||
|
||||
// SQLite APIs
|
||||
|
||||
int (*sqlite3_open_v2)(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs);
|
||||
int (*sqlite3_close)(sqlite3 *db);
|
||||
static int (*sqlite3_open_v2)(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs);
|
||||
static int (*sqlite3_close)(sqlite3 *db);
|
||||
const char *(*sqlite3_errstr)(int);
|
||||
|
||||
static int (*sqlite3_prepare_v2)(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail);
|
||||
static int (*sqlite3_bind_parameter_count)(sqlite3_stmt*);
|
||||
static int (*sqlite3_bind_int64)(sqlite3_stmt*, int, int64_t);
|
||||
@ -44,7 +50,7 @@ constexpr char apex_path[] = "/apex/com.android.runtime/lib64:/apex/com.android.
|
||||
constexpr char apex_path[] = "/apex/com.android.runtime/lib:/apex/com.android.art/lib:/apex/com.android.i18n/lib:";
|
||||
#endif
|
||||
|
||||
bool load_sqlite() {
|
||||
static bool load_sqlite() {
|
||||
static int dl_init = 0;
|
||||
if (dl_init)
|
||||
return dl_init > 0;
|
||||
@ -96,7 +102,11 @@ using sql_exec_callback_real = void(*)(void*, StringSlice, sqlite3_stmt*);
|
||||
|
||||
#define sql_chk(fn, ...) if (int rc = fn(__VA_ARGS__); rc != SQLITE_OK) return rc
|
||||
|
||||
int sql_exec(sqlite3 *db, rust::Str zSql, sql_bind_callback bind_cb, void *bind_cookie, sql_exec_callback exec_cb, void *exec_cookie) {
|
||||
// Exports to Rust
|
||||
extern "C" int sql_exec_impl(
|
||||
sqlite3 *db, rust::Str zSql,
|
||||
sql_bind_callback bind_cb = nullptr, void *bind_cookie = nullptr,
|
||||
sql_exec_callback exec_cb = nullptr, void *exec_cookie = nullptr) {
|
||||
const char *sql = zSql.begin();
|
||||
unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)> stmt(nullptr, sqlite3_finalize);
|
||||
|
||||
@ -142,12 +152,12 @@ int sql_exec(sqlite3 *db, rust::Str zSql, sql_bind_callback bind_cb, void *bind_
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int DbValues::get_int(int index) {
|
||||
return sqlite3_column_int(reinterpret_cast<sqlite3_stmt*>(this), index);
|
||||
int DbValues::get_int(int index) const {
|
||||
return sqlite3_column_int((sqlite3_stmt*) this, index);
|
||||
}
|
||||
|
||||
const char *DbValues::get_text(int index) {
|
||||
return sqlite3_column_text(reinterpret_cast<sqlite3_stmt*>(this), index);
|
||||
const char *DbValues::get_text(int index) const {
|
||||
return sqlite3_column_text((sqlite3_stmt*) this, index);
|
||||
}
|
||||
|
||||
int DbStatement::bind_int64(int index, int64_t val) {
|
||||
@ -157,3 +167,186 @@ int DbStatement::bind_int64(int index, int64_t val) {
|
||||
int DbStatement::bind_text(int index, rust::Str val) {
|
||||
return sqlite3_bind_text(reinterpret_cast<sqlite3_stmt*>(this), index, val.data(), val.size(), nullptr);
|
||||
}
|
||||
|
||||
#define sql_chk_log(fn, ...) if (int rc = fn(__VA_ARGS__); rc != SQLITE_OK) { \
|
||||
LOGE("sqlite3(db.cpp:%d): %s\n", __LINE__, sqlite3_errstr(rc)); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
static bool open_and_init_db_impl(sqlite3 **dbOut) {
|
||||
if (!load_sqlite()) {
|
||||
LOGE("sqlite3: Cannot load libsqlite.so\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
unique_ptr<sqlite3, decltype(sqlite3_close)> db(nullptr, sqlite3_close);
|
||||
{
|
||||
sqlite3 *sql;
|
||||
sql_chk_log(sqlite3_open_v2, MAGISKDB, &sql,
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, nullptr);
|
||||
db.reset(sql);
|
||||
}
|
||||
|
||||
int ver = 0;
|
||||
bool upgrade = false;
|
||||
auto ver_cb = [](void *ver, auto, DbValues &data) {
|
||||
*static_cast<int *>(ver) = data.get_int(0);
|
||||
};
|
||||
sql_chk_log(sql_exec_impl, db.get(), "PRAGMA user_version", nullptr, nullptr, ver_cb, &ver);
|
||||
if (ver > DB_VERSION) {
|
||||
// Don't support downgrading database
|
||||
LOGE("sqlite3: Downgrading database is not supported\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto create_policy = [&] {
|
||||
return sql_exec_impl(db.get(),
|
||||
"CREATE TABLE IF NOT EXISTS policies "
|
||||
"(uid INT, policy INT, until INT, logging INT, "
|
||||
"notification INT, PRIMARY KEY(uid))");
|
||||
};
|
||||
auto create_settings = [&] {
|
||||
return sql_exec_impl(db.get(),
|
||||
"CREATE TABLE IF NOT EXISTS settings "
|
||||
"(key TEXT, value INT, PRIMARY KEY(key))");
|
||||
};
|
||||
auto create_strings = [&] {
|
||||
return sql_exec_impl(db.get(),
|
||||
"CREATE TABLE IF NOT EXISTS strings "
|
||||
"(key TEXT, value TEXT, PRIMARY KEY(key))");
|
||||
};
|
||||
auto create_denylist = [&] {
|
||||
return sql_exec_impl(db.get(),
|
||||
"CREATE TABLE IF NOT EXISTS denylist "
|
||||
"(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process))");
|
||||
};
|
||||
|
||||
// Database changelog:
|
||||
//
|
||||
// 0 - 6: DB stored in app private data. There are no longer any code in the project to
|
||||
// migrate these data, so no need to take any of these versions into consideration.
|
||||
// 7 : create table `hidelist` (process TEXT, PRIMARY KEY(process))
|
||||
// 8 : add new column (package_name TEXT) to table `hidelist`
|
||||
// 9 : rebuild table `hidelist` to change primary key (PRIMARY KEY(package_name, process))
|
||||
// 10: remove table `logs`
|
||||
// 11: remove table `hidelist` and create table `denylist` (same data structure)
|
||||
// 12: rebuild table `policies` to drop column `package_name`
|
||||
|
||||
if (/* 0, 1, 2, 3, 4, 5, 6 */ ver <= 6) {
|
||||
sql_chk_log(create_policy);
|
||||
sql_chk_log(create_settings);
|
||||
sql_chk_log(create_strings);
|
||||
sql_chk_log(create_denylist);
|
||||
|
||||
// Directly jump to latest
|
||||
ver = DB_VERSION;
|
||||
upgrade = true;
|
||||
}
|
||||
if (ver == 7) {
|
||||
sql_chk_log(sql_exec_impl, db.get(),
|
||||
"BEGIN TRANSACTION;"
|
||||
"ALTER TABLE hidelist RENAME TO hidelist_tmp;"
|
||||
"CREATE TABLE IF NOT EXISTS hidelist "
|
||||
"(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process));"
|
||||
"INSERT INTO hidelist SELECT process as package_name, process FROM hidelist_tmp;"
|
||||
"DROP TABLE hidelist_tmp;"
|
||||
"COMMIT;");
|
||||
// Directly jump to version 9
|
||||
ver = 9;
|
||||
upgrade = true;
|
||||
}
|
||||
if (ver == 8) {
|
||||
sql_chk_log(sql_exec_impl, db.get(),
|
||||
"BEGIN TRANSACTION;"
|
||||
"ALTER TABLE hidelist RENAME TO hidelist_tmp;"
|
||||
"CREATE TABLE IF NOT EXISTS hidelist "
|
||||
"(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process));"
|
||||
"INSERT INTO hidelist SELECT * FROM hidelist_tmp;"
|
||||
"DROP TABLE hidelist_tmp;"
|
||||
"COMMIT;");
|
||||
ver = 9;
|
||||
upgrade = true;
|
||||
}
|
||||
if (ver == 9) {
|
||||
sql_chk_log(sql_exec_impl, db.get(), "DROP TABLE IF EXISTS logs", nullptr, nullptr);
|
||||
ver = 10;
|
||||
upgrade = true;
|
||||
}
|
||||
if (ver == 10) {
|
||||
sql_chk_log(sql_exec_impl, db.get(),
|
||||
"DROP TABLE IF EXISTS hidelist;"
|
||||
"DELETE FROM settings WHERE key='magiskhide';");
|
||||
sql_chk_log(create_denylist);
|
||||
ver = 11;
|
||||
upgrade = true;
|
||||
}
|
||||
if (ver == 11) {
|
||||
sql_chk_log(sql_exec_impl, db.get(),
|
||||
"BEGIN TRANSACTION;"
|
||||
"ALTER TABLE policies RENAME TO policies_tmp;"
|
||||
"CREATE TABLE IF NOT EXISTS policies "
|
||||
"(uid INT, policy INT, until INT, logging INT, "
|
||||
"notification INT, PRIMARY KEY(uid));"
|
||||
"INSERT INTO policies "
|
||||
"SELECT uid, policy, until, logging, notification FROM policies_tmp;"
|
||||
"DROP TABLE policies_tmp;"
|
||||
"COMMIT;");
|
||||
ver = 12;
|
||||
upgrade = true;
|
||||
}
|
||||
|
||||
if (upgrade) {
|
||||
// Set version
|
||||
sql_chk_log(sql_exec_impl, db.get(), "PRAGMA user_version=" DB_VERSION_STR);
|
||||
}
|
||||
|
||||
*dbOut = db.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
sqlite3 *open_and_init_db() {
|
||||
sqlite3 *db = nullptr;
|
||||
return open_and_init_db_impl(&db) ? db : nullptr;
|
||||
}
|
||||
|
||||
// Exported from Rust
|
||||
extern "C" int sql_exec_rs(
|
||||
rust::Str zSql,
|
||||
sql_bind_callback bind_cb, void *bind_cookie,
|
||||
sql_exec_callback exec_cb, void *exec_cookie);
|
||||
|
||||
bool db_exec(const char *sql, DbArgs args, db_exec_callback exec_fn) {
|
||||
using db_bind_callback = std::function<int(int, DbStatement&)>;
|
||||
|
||||
db_bind_callback bind_fn = {};
|
||||
sql_bind_callback bind_cb = nullptr;
|
||||
if (!args.empty()) {
|
||||
bind_fn = std::ref(args);
|
||||
bind_cb = [](void *v, int index, DbStatement &stmt) -> int {
|
||||
auto fn = static_cast<db_bind_callback*>(v);
|
||||
return fn->operator()(index, stmt);
|
||||
};
|
||||
}
|
||||
sql_exec_callback exec_cb = nullptr;
|
||||
if (exec_fn) {
|
||||
exec_cb = [](void *v, StringSlice columns, DbValues &data) {
|
||||
auto fn = static_cast<db_exec_callback*>(v);
|
||||
fn->operator()(columns, data);
|
||||
};
|
||||
}
|
||||
sql_chk_log(sql_exec_rs, sql, bind_cb, &bind_fn, exec_cb, &exec_fn);
|
||||
return true;
|
||||
}
|
||||
|
||||
int DbArgs::operator()(int index, DbStatement &stmt) {
|
||||
if (curr < args.size()) {
|
||||
const auto &arg = args[curr++];
|
||||
switch (arg.type) {
|
||||
case DbArg::INT:
|
||||
return stmt.bind_int64(index, arg.int_val);
|
||||
case DbArg::TEXT:
|
||||
return stmt.bind_text(index, arg.str_val);
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <memory>
|
||||
|
||||
#include <db.hpp>
|
||||
#include <sqlite.hpp>
|
||||
#include <core.hpp>
|
||||
|
||||
#define DEFAULT_SHELL "/system/bin/sh"
|
||||
@ -47,7 +47,7 @@ public:
|
||||
|
||||
// These should be guarded with internal lock
|
||||
int eval_uid; // The effective UID, taking multiuser settings into consideration
|
||||
db_settings cfg;
|
||||
struct DbSettings cfg;
|
||||
su_access access;
|
||||
std::string mgr_pkg;
|
||||
int mgr_uid;
|
||||
|
@ -18,7 +18,8 @@ static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static shared_ptr<su_info> cached;
|
||||
|
||||
su_info::su_info(int uid) :
|
||||
uid(uid), eval_uid(-1), mgr_uid(-1), timestamp(0), _lock(PTHREAD_MUTEX_INITIALIZER) {}
|
||||
uid(uid), eval_uid(-1), cfg(DbSettings()),
|
||||
mgr_uid(-1), timestamp(0), _lock(PTHREAD_MUTEX_INITIALIZER) {}
|
||||
|
||||
su_info::~su_info() {
|
||||
pthread_mutex_destroy(&_lock);
|
||||
@ -57,20 +58,20 @@ void su_access::operator()(StringSlice columns, DbValues &data) {
|
||||
|
||||
void su_info::check_db() {
|
||||
eval_uid = uid;
|
||||
get_db_settings(cfg);
|
||||
MagiskD().get_db_settings(cfg);
|
||||
|
||||
// Check multiuser settings
|
||||
switch (cfg.multiuser_mode) {
|
||||
case MULTIUSER_MODE_OWNER_ONLY:
|
||||
case MultiuserMode::OwnerOnly:
|
||||
if (to_user_id(uid) != 0) {
|
||||
eval_uid = -1;
|
||||
access.silent_deny();
|
||||
}
|
||||
break;
|
||||
case MULTIUSER_MODE_OWNER_MANAGED:
|
||||
case MultiuserMode::OwnerManaged:
|
||||
eval_uid = to_app_id(uid);
|
||||
break;
|
||||
case MULTIUSER_MODE_USER:
|
||||
case MultiuserMode::User:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -93,35 +94,35 @@ bool uid_granted_root(int uid) {
|
||||
if (uid == AID_ROOT)
|
||||
return true;
|
||||
|
||||
db_settings cfg;
|
||||
get_db_settings(cfg);
|
||||
auto cfg = DbSettings();
|
||||
MagiskD().get_db_settings(cfg);
|
||||
|
||||
// Check user root access settings
|
||||
switch (cfg.root_access) {
|
||||
case ROOT_ACCESS_DISABLED:
|
||||
case RootAccess::Disabled:
|
||||
return false;
|
||||
case ROOT_ACCESS_APPS_ONLY:
|
||||
case RootAccess::AppsOnly:
|
||||
if (uid == AID_SHELL)
|
||||
return false;
|
||||
break;
|
||||
case ROOT_ACCESS_ADB_ONLY:
|
||||
case RootAccess::AdbOnly:
|
||||
if (uid != AID_SHELL)
|
||||
return false;
|
||||
break;
|
||||
case ROOT_ACCESS_APPS_AND_ADB:
|
||||
case RootAccess::AppsAndAdb:
|
||||
break;
|
||||
}
|
||||
|
||||
// Check multiuser settings
|
||||
switch (cfg.multiuser_mode) {
|
||||
case MULTIUSER_MODE_OWNER_ONLY:
|
||||
case MultiuserMode::OwnerOnly:
|
||||
if (to_user_id(uid) != 0)
|
||||
return false;
|
||||
break;
|
||||
case MULTIUSER_MODE_OWNER_MANAGED:
|
||||
case MultiuserMode::OwnerManaged:
|
||||
uid = to_app_id(uid);
|
||||
break;
|
||||
case MULTIUSER_MODE_USER:
|
||||
case MultiuserMode::User:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -193,23 +194,23 @@ static shared_ptr<su_info> get_su_info(unsigned uid) {
|
||||
|
||||
// Check su access settings
|
||||
switch (info->cfg.root_access) {
|
||||
case ROOT_ACCESS_DISABLED:
|
||||
case RootAccess::Disabled:
|
||||
LOGW("Root access is disabled!\n");
|
||||
info->access.silent_deny();
|
||||
break;
|
||||
case ROOT_ACCESS_ADB_ONLY:
|
||||
case RootAccess::AdbOnly:
|
||||
if (info->uid != AID_SHELL) {
|
||||
LOGW("Root access limited to ADB only!\n");
|
||||
info->access.silent_deny();
|
||||
}
|
||||
break;
|
||||
case ROOT_ACCESS_APPS_ONLY:
|
||||
case RootAccess::AppsOnly:
|
||||
if (info->uid == AID_SHELL) {
|
||||
LOGW("Root access is disabled for ADB!\n");
|
||||
info->access.silent_deny();
|
||||
}
|
||||
break;
|
||||
case ROOT_ACCESS_APPS_AND_ADB:
|
||||
case RootAccess::AppsAndAdb:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -395,19 +396,19 @@ void su_daemon_handler(int client, const sock_cred *cred) {
|
||||
if (ctx.req.target == -1)
|
||||
ctx.req.target = ctx.pid;
|
||||
else if (ctx.req.target == 0)
|
||||
ctx.info->cfg.mnt_ns = NAMESPACE_MODE_GLOBAL;
|
||||
else if (ctx.info->cfg.mnt_ns == NAMESPACE_MODE_GLOBAL)
|
||||
ctx.info->cfg.mnt_ns = NAMESPACE_MODE_REQUESTER;
|
||||
ctx.info->cfg.mnt_ns = MntNsMode::Global;
|
||||
else if (ctx.info->cfg.mnt_ns == MntNsMode::Global)
|
||||
ctx.info->cfg.mnt_ns = MntNsMode::Requester;
|
||||
switch (ctx.info->cfg.mnt_ns) {
|
||||
case NAMESPACE_MODE_GLOBAL:
|
||||
case MntNsMode::Global:
|
||||
LOGD("su: use global namespace\n");
|
||||
break;
|
||||
case NAMESPACE_MODE_REQUESTER:
|
||||
case MntNsMode::Requester:
|
||||
LOGD("su: use namespace of pid=[%d]\n", ctx.req.target);
|
||||
if (switch_mnt_ns(ctx.req.target))
|
||||
LOGD("su: setns failed, fallback to global\n");
|
||||
break;
|
||||
case NAMESPACE_MODE_ISOLATE:
|
||||
case MntNsMode::Isolate:
|
||||
LOGD("su: use new isolated namespace\n");
|
||||
switch_mnt_ns(ctx.req.target);
|
||||
xunshare(CLONE_NEWNS);
|
||||
|
Loading…
x
Reference in New Issue
Block a user