mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-04-30 22:44:25 +02: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/daemon.cpp \
|
||||||
core/bootstages.cpp \
|
core/bootstages.cpp \
|
||||||
core/socket.cpp \
|
core/socket.cpp \
|
||||||
core/db.cpp \
|
|
||||||
core/package.cpp \
|
core/package.cpp \
|
||||||
core/scripting.cpp \
|
core/scripting.cpp \
|
||||||
core/selinux.cpp \
|
core/selinux.cpp \
|
||||||
|
1
native/src/Cargo.lock
generated
1
native/src/Cargo.lock
generated
@ -449,6 +449,7 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
"pb-rs",
|
"pb-rs",
|
||||||
"quick-protobuf",
|
"quick-protobuf",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -340,6 +340,11 @@ impl Utf8CStr {
|
|||||||
Self::from_cstr(unsafe { CStr::from_ptr(ptr) })
|
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)]
|
#[inline(always)]
|
||||||
pub fn as_bytes_with_nul(&self) -> &[u8] {
|
pub fn as_bytes_with_nul(&self) -> &[u8] {
|
||||||
&self.0
|
&self.0
|
||||||
|
@ -18,3 +18,4 @@ num-traits = { workspace = true }
|
|||||||
num-derive = { workspace = true }
|
num-derive = { workspace = true }
|
||||||
quick-protobuf = { workspace = true }
|
quick-protobuf = { workspace = true }
|
||||||
bytemuck = { workspace = true, features = ["derive"] }
|
bytemuck = { workspace = true, features = ["derive"] }
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <consts.hpp>
|
#include <consts.hpp>
|
||||||
#include <db.hpp>
|
#include <sqlite.hpp>
|
||||||
#include <base.hpp>
|
#include <base.hpp>
|
||||||
#include <core.hpp>
|
#include <core.hpp>
|
||||||
#include <selinux.hpp>
|
#include <selinux.hpp>
|
||||||
@ -133,17 +133,6 @@ static bool check_key_combo() {
|
|||||||
return true;
|
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 *
|
* Boot Stage Handlers *
|
||||||
***********************/
|
***********************/
|
||||||
@ -155,15 +144,12 @@ bool MagiskD::post_fs_data() const noexcept {
|
|||||||
|
|
||||||
preserve_stub_apk();
|
preserve_stub_apk();
|
||||||
|
|
||||||
bool safe_mode = false;
|
|
||||||
|
|
||||||
if (access(SECURE_DIR, F_OK) != 0) {
|
if (access(SECURE_DIR, F_OK) != 0) {
|
||||||
if (SDK_INT < 24) {
|
if (SDK_INT < 24) {
|
||||||
xmkdir(SECURE_DIR, 0700);
|
xmkdir(SECURE_DIR, 0700);
|
||||||
} else {
|
} else {
|
||||||
LOGE(SECURE_DIR " is not present, abort\n");
|
LOGE(SECURE_DIR " is not present, abort\n");
|
||||||
safe_mode = true;
|
return true;
|
||||||
return safe_mode;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,28 +157,31 @@ bool MagiskD::post_fs_data() const noexcept {
|
|||||||
|
|
||||||
if (!magisk_env()) {
|
if (!magisk_env()) {
|
||||||
LOGE("* Magisk environment incomplete, abort\n");
|
LOGE("* Magisk environment incomplete, abort\n");
|
||||||
safe_mode = true;
|
return true;
|
||||||
return safe_mode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
LOGI("* Safe mode triggered\n");
|
||||||
safe_mode = true;
|
|
||||||
// Disable all modules and zygisk so next boot will be clean
|
// Disable all modules and zygisk so next boot will be clean
|
||||||
disable_modules();
|
disable_modules();
|
||||||
set_db_settings(ZYGISK_CONFIG, false);
|
set_db_setting(DbEntryKey::ZygiskConfig, false);
|
||||||
return safe_mode;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
exec_common_scripts("post-fs-data");
|
exec_common_scripts("post-fs-data");
|
||||||
db_settings dbs;
|
zygisk_enabled = get_db_setting(DbEntryKey::ZygiskConfig);
|
||||||
get_db_settings(dbs, ZYGISK_CONFIG);
|
|
||||||
zygisk_enabled = dbs.zygisk;
|
|
||||||
initialize_denylist();
|
initialize_denylist();
|
||||||
setup_mounts();
|
setup_mounts();
|
||||||
handle_modules();
|
handle_modules();
|
||||||
load_modules();
|
load_modules();
|
||||||
return safe_mode;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MagiskD::late_start() const noexcept {
|
void MagiskD::late_start() const noexcept {
|
||||||
@ -210,7 +199,7 @@ void MagiskD::boot_complete() const noexcept {
|
|||||||
LOGI("** boot-complete triggered\n");
|
LOGI("** boot-complete triggered\n");
|
||||||
|
|
||||||
// Reset the bootloop counter once we have boot-complete
|
// 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
|
// At this point it's safe to create the folder
|
||||||
if (access(SECURE_DIR, F_OK) != 0)
|
if (access(SECURE_DIR, F_OK) != 0)
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#include <base.hpp>
|
#include <base.hpp>
|
||||||
#include <core.hpp>
|
#include <core.hpp>
|
||||||
#include <selinux.hpp>
|
#include <selinux.hpp>
|
||||||
#include <db.hpp>
|
#include <sqlite.hpp>
|
||||||
#include <flags.h>
|
#include <flags.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -151,7 +151,7 @@ static void handle_request_async(int client, int code, const sock_cred &cred) {
|
|||||||
close(client);
|
close(client);
|
||||||
break;
|
break;
|
||||||
case +RequestCode::SQLITE_CMD:
|
case +RequestCode::SQLITE_CMD:
|
||||||
exec_sql(client);
|
MagiskD().db_exec(client);
|
||||||
break;
|
break;
|
||||||
case +RequestCode::REMOVE_MODULES: {
|
case +RequestCode::REMOVE_MODULES: {
|
||||||
int do_reboot = read_int(client);
|
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::libc::{O_CLOEXEC, O_RDONLY};
|
||||||
use base::{
|
use base::{
|
||||||
cstr, libc, open_fd, BufReadExt, Directory, FsPathBuf, ResultExt, Utf8CStr, Utf8CStrBuf,
|
cstr, libc, open_fd, BufReadExt, Directory, FsPathBuf, ReadExt, ResultExt, Utf8CStr,
|
||||||
Utf8CStrBufArr, Utf8CStrBufRef, WalkResult,
|
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::consts::MAIN_CONFIG;
|
||||||
|
use crate::db::Sqlite3;
|
||||||
use crate::ffi::{get_magisk_tmp, RequestCode};
|
use crate::ffi::{get_magisk_tmp, RequestCode};
|
||||||
use crate::get_prop;
|
use crate::get_prop;
|
||||||
use crate::logging::magisk_logging;
|
use crate::logging::magisk_logging;
|
||||||
@ -42,6 +43,7 @@ impl BootStateFlags {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MagiskD {
|
pub struct MagiskD {
|
||||||
pub logd: Mutex<Option<File>>,
|
pub logd: Mutex<Option<File>>,
|
||||||
|
pub sql_connection: Mutex<Option<Sqlite3>>,
|
||||||
boot_stage_lock: Mutex<BootStateFlags>,
|
boot_stage_lock: Mutex<BootStateFlags>,
|
||||||
is_emulator: bool,
|
is_emulator: bool,
|
||||||
is_recovery: bool,
|
is_recovery: bool,
|
||||||
@ -187,3 +189,39 @@ pub fn find_apk_path(pkg: &Utf8CStr, data: &mut [u8]) -> usize {
|
|||||||
.log()
|
.log()
|
||||||
.unwrap_or(0)
|
.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 <consts.hpp>
|
||||||
#include <base.hpp>
|
#include <base.hpp>
|
||||||
#include <db.hpp>
|
#include <sqlite.hpp>
|
||||||
#include <core.hpp>
|
#include <core.hpp>
|
||||||
|
|
||||||
#include "deny.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;
|
return DenyResponse::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,15 +393,13 @@ int disable_deny() {
|
|||||||
if (denylist_enforced.exchange(false)) {
|
if (denylist_enforced.exchange(false)) {
|
||||||
LOGI("* Disable DenyList\n");
|
LOGI("* Disable DenyList\n");
|
||||||
}
|
}
|
||||||
set_db_settings(DENYLIST_CONFIG, false);
|
MagiskD().set_db_setting(DbEntryKey::DenylistConfig, false);
|
||||||
return DenyResponse::OK;
|
return DenyResponse::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void initialize_denylist() {
|
void initialize_denylist() {
|
||||||
if (!denylist_enforced) {
|
if (!denylist_enforced) {
|
||||||
db_settings dbs;
|
if (MagiskD().get_db_setting(DbEntryKey::DenylistConfig))
|
||||||
get_db_settings(dbs, DENYLIST_CONFIG);
|
|
||||||
if (dbs.denylist)
|
|
||||||
enable_deny();
|
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
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include <cxx.h>
|
#include <cxx.h>
|
||||||
|
|
||||||
#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */
|
#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */
|
||||||
@ -13,14 +15,13 @@
|
|||||||
struct sqlite3;
|
struct sqlite3;
|
||||||
struct sqlite3_stmt;
|
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);
|
extern const char *(*sqlite3_errstr)(int);
|
||||||
|
|
||||||
// Transparent wrappers of sqlite3_stmt
|
// Transparent wrappers of sqlite3_stmt
|
||||||
struct DbValues {
|
struct DbValues {
|
||||||
const char *get_text(int index);
|
const char *get_text(int index) const;
|
||||||
int get_int(int index);
|
rust::Str get_str(int index) const { return get_text(index); }
|
||||||
|
int get_int(int index) const;
|
||||||
~DbValues() = delete;
|
~DbValues() = delete;
|
||||||
};
|
};
|
||||||
struct DbStatement {
|
struct DbStatement {
|
||||||
@ -33,8 +34,43 @@ using StringSlice = rust::Slice<rust::String>;
|
|||||||
using sql_bind_callback = int(*)(void*, int, DbStatement&);
|
using sql_bind_callback = int(*)(void*, int, DbStatement&);
|
||||||
using sql_exec_callback = void(*)(void*, StringSlice, DbValues&);
|
using sql_exec_callback = void(*)(void*, StringSlice, DbValues&);
|
||||||
|
|
||||||
bool load_sqlite();
|
|
||||||
sqlite3 *open_and_init_db();
|
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(format_args_nl)]
|
||||||
#![feature(try_blocks)]
|
#![feature(try_blocks)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
#![feature(fn_traits)]
|
||||||
#![allow(clippy::missing_safety_doc)]
|
#![allow(clippy::missing_safety_doc)]
|
||||||
|
|
||||||
use base::Utf8CStr;
|
use base::Utf8CStr;
|
||||||
use cert::read_certificate;
|
use cert::read_certificate;
|
||||||
use daemon::{daemon_entry, find_apk_path, get_magiskd, MagiskD};
|
use daemon::{daemon_entry, find_apk_path, get_magiskd, MagiskD};
|
||||||
|
use db::get_default_db_settings;
|
||||||
use logging::{
|
use logging::{
|
||||||
android_logging, magisk_logging, zygisk_close_logd, zygisk_get_logd, zygisk_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};
|
use resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop};
|
||||||
|
|
||||||
mod cert;
|
mod cert;
|
||||||
#[path = "../include/consts.rs"]
|
#[path = "../include/consts.rs"]
|
||||||
mod consts;
|
mod consts;
|
||||||
mod daemon;
|
mod daemon;
|
||||||
|
mod db;
|
||||||
mod logging;
|
mod logging;
|
||||||
mod mount;
|
mod mount;
|
||||||
mod resetprop;
|
mod resetprop;
|
||||||
@ -76,6 +79,67 @@ pub mod ffi {
|
|||||||
fn switch_mnt_ns(pid: i32) -> i32;
|
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" {
|
extern "Rust" {
|
||||||
fn rust_test_entry();
|
fn rust_test_entry();
|
||||||
fn android_logging();
|
fn android_logging();
|
||||||
@ -102,10 +166,22 @@ pub mod ffi {
|
|||||||
extern "Rust" {
|
extern "Rust" {
|
||||||
type MagiskD;
|
type MagiskD;
|
||||||
fn setup_logfile(self: &MagiskD);
|
fn setup_logfile(self: &MagiskD);
|
||||||
fn is_emulator(self: &MagiskD) -> bool;
|
|
||||||
fn is_recovery(self: &MagiskD) -> bool;
|
fn is_recovery(self: &MagiskD) -> bool;
|
||||||
fn boot_stage_handler(self: &MagiskD, client: i32, code: i32);
|
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"]
|
#[cxx_name = "MagiskD"]
|
||||||
fn get_magiskd() -> &'static MagiskD;
|
fn get_magiskd() -> &'static MagiskD;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include <base.hpp>
|
#include <base.hpp>
|
||||||
#include <consts.hpp>
|
#include <consts.hpp>
|
||||||
#include <core.hpp>
|
#include <core.hpp>
|
||||||
#include <db.hpp>
|
#include <sqlite.hpp>
|
||||||
#include <flags.h>
|
#include <flags.h>
|
||||||
|
|
||||||
using namespace std;
|
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) {
|
int get_manager(int user, string *pkg, bool install) {
|
||||||
mutex_guard g(pkg_lock);
|
mutex_guard g(pkg_lock);
|
||||||
|
|
||||||
db_strings str;
|
string db_pkg = (string) MagiskD().get_db_string(DbEntryKey::SuManager);
|
||||||
get_db_strings(str, SU_MANAGER);
|
|
||||||
string db_pkg(std::move(str.su_manager));
|
|
||||||
|
|
||||||
// If database changed, always re-check files
|
// If database changed, always re-check files
|
||||||
if (db_pkg != repackaged_pkg) {
|
if (db_pkg != repackaged_pkg) {
|
||||||
@ -251,7 +249,7 @@ int get_manager(int user, string *pkg, bool install) {
|
|||||||
case CERT_MISMATCH:
|
case CERT_MISMATCH:
|
||||||
install = true;
|
install = true;
|
||||||
case NOT_INSTALLED:
|
case NOT_INSTALLED:
|
||||||
rm_db_strings(SU_MANAGER);
|
MagiskD().rm_db_string(DbEntryKey::SuManager);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,22 @@
|
|||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
#include <consts.hpp>
|
||||||
#include <base.hpp>
|
#include <base.hpp>
|
||||||
#include <sqlite.hpp>
|
#include <sqlite.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
#define DB_VERSION 12
|
||||||
|
#define DB_VERSION_STR "12"
|
||||||
|
|
||||||
|
#define DBLOGV(...)
|
||||||
|
//#define DBLOGV(...) LOGD("magiskdb: " __VA_ARGS__)
|
||||||
|
|
||||||
// SQLite APIs
|
// SQLite APIs
|
||||||
|
|
||||||
int (*sqlite3_open_v2)(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs);
|
static int (*sqlite3_open_v2)(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs);
|
||||||
int (*sqlite3_close)(sqlite3 *db);
|
static int (*sqlite3_close)(sqlite3 *db);
|
||||||
const char *(*sqlite3_errstr)(int);
|
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_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_parameter_count)(sqlite3_stmt*);
|
||||||
static int (*sqlite3_bind_int64)(sqlite3_stmt*, int, int64_t);
|
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:";
|
constexpr char apex_path[] = "/apex/com.android.runtime/lib:/apex/com.android.art/lib:/apex/com.android.i18n/lib:";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool load_sqlite() {
|
static bool load_sqlite() {
|
||||||
static int dl_init = 0;
|
static int dl_init = 0;
|
||||||
if (dl_init)
|
if (dl_init)
|
||||||
return dl_init > 0;
|
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
|
#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();
|
const char *sql = zSql.begin();
|
||||||
unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)> stmt(nullptr, sqlite3_finalize);
|
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;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DbValues::get_int(int index) {
|
int DbValues::get_int(int index) const {
|
||||||
return sqlite3_column_int(reinterpret_cast<sqlite3_stmt*>(this), index);
|
return sqlite3_column_int((sqlite3_stmt*) this, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *DbValues::get_text(int index) {
|
const char *DbValues::get_text(int index) const {
|
||||||
return sqlite3_column_text(reinterpret_cast<sqlite3_stmt*>(this), index);
|
return sqlite3_column_text((sqlite3_stmt*) this, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
int DbStatement::bind_int64(int index, int64_t val) {
|
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) {
|
int DbStatement::bind_text(int index, rust::Str val) {
|
||||||
return sqlite3_bind_text(reinterpret_cast<sqlite3_stmt*>(this), index, val.data(), val.size(), nullptr);
|
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 <sys/stat.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <db.hpp>
|
#include <sqlite.hpp>
|
||||||
#include <core.hpp>
|
#include <core.hpp>
|
||||||
|
|
||||||
#define DEFAULT_SHELL "/system/bin/sh"
|
#define DEFAULT_SHELL "/system/bin/sh"
|
||||||
@ -47,7 +47,7 @@ public:
|
|||||||
|
|
||||||
// These should be guarded with internal lock
|
// These should be guarded with internal lock
|
||||||
int eval_uid; // The effective UID, taking multiuser settings into consideration
|
int eval_uid; // The effective UID, taking multiuser settings into consideration
|
||||||
db_settings cfg;
|
struct DbSettings cfg;
|
||||||
su_access access;
|
su_access access;
|
||||||
std::string mgr_pkg;
|
std::string mgr_pkg;
|
||||||
int mgr_uid;
|
int mgr_uid;
|
||||||
|
@ -18,7 +18,8 @@ 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(int uid) :
|
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() {
|
su_info::~su_info() {
|
||||||
pthread_mutex_destroy(&_lock);
|
pthread_mutex_destroy(&_lock);
|
||||||
@ -57,20 +58,20 @@ void su_access::operator()(StringSlice columns, DbValues &data) {
|
|||||||
|
|
||||||
void su_info::check_db() {
|
void su_info::check_db() {
|
||||||
eval_uid = uid;
|
eval_uid = uid;
|
||||||
get_db_settings(cfg);
|
MagiskD().get_db_settings(cfg);
|
||||||
|
|
||||||
// Check multiuser settings
|
// Check multiuser settings
|
||||||
switch (cfg.multiuser_mode) {
|
switch (cfg.multiuser_mode) {
|
||||||
case MULTIUSER_MODE_OWNER_ONLY:
|
case MultiuserMode::OwnerOnly:
|
||||||
if (to_user_id(uid) != 0) {
|
if (to_user_id(uid) != 0) {
|
||||||
eval_uid = -1;
|
eval_uid = -1;
|
||||||
access.silent_deny();
|
access.silent_deny();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MULTIUSER_MODE_OWNER_MANAGED:
|
case MultiuserMode::OwnerManaged:
|
||||||
eval_uid = to_app_id(uid);
|
eval_uid = to_app_id(uid);
|
||||||
break;
|
break;
|
||||||
case MULTIUSER_MODE_USER:
|
case MultiuserMode::User:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -93,35 +94,35 @@ bool uid_granted_root(int uid) {
|
|||||||
if (uid == AID_ROOT)
|
if (uid == AID_ROOT)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
db_settings cfg;
|
auto cfg = DbSettings();
|
||||||
get_db_settings(cfg);
|
MagiskD().get_db_settings(cfg);
|
||||||
|
|
||||||
// Check user root access settings
|
// Check user root access settings
|
||||||
switch (cfg.root_access) {
|
switch (cfg.root_access) {
|
||||||
case ROOT_ACCESS_DISABLED:
|
case RootAccess::Disabled:
|
||||||
return false;
|
return false;
|
||||||
case ROOT_ACCESS_APPS_ONLY:
|
case RootAccess::AppsOnly:
|
||||||
if (uid == AID_SHELL)
|
if (uid == AID_SHELL)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case ROOT_ACCESS_ADB_ONLY:
|
case RootAccess::AdbOnly:
|
||||||
if (uid != AID_SHELL)
|
if (uid != AID_SHELL)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case ROOT_ACCESS_APPS_AND_ADB:
|
case RootAccess::AppsAndAdb:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check multiuser settings
|
// Check multiuser settings
|
||||||
switch (cfg.multiuser_mode) {
|
switch (cfg.multiuser_mode) {
|
||||||
case MULTIUSER_MODE_OWNER_ONLY:
|
case MultiuserMode::OwnerOnly:
|
||||||
if (to_user_id(uid) != 0)
|
if (to_user_id(uid) != 0)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
case MULTIUSER_MODE_OWNER_MANAGED:
|
case MultiuserMode::OwnerManaged:
|
||||||
uid = to_app_id(uid);
|
uid = to_app_id(uid);
|
||||||
break;
|
break;
|
||||||
case MULTIUSER_MODE_USER:
|
case MultiuserMode::User:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -193,23 +194,23 @@ static shared_ptr<su_info> get_su_info(unsigned uid) {
|
|||||||
|
|
||||||
// Check su access settings
|
// Check su access settings
|
||||||
switch (info->cfg.root_access) {
|
switch (info->cfg.root_access) {
|
||||||
case ROOT_ACCESS_DISABLED:
|
case RootAccess::Disabled:
|
||||||
LOGW("Root access is disabled!\n");
|
LOGW("Root access is disabled!\n");
|
||||||
info->access.silent_deny();
|
info->access.silent_deny();
|
||||||
break;
|
break;
|
||||||
case ROOT_ACCESS_ADB_ONLY:
|
case RootAccess::AdbOnly:
|
||||||
if (info->uid != AID_SHELL) {
|
if (info->uid != AID_SHELL) {
|
||||||
LOGW("Root access limited to ADB only!\n");
|
LOGW("Root access limited to ADB only!\n");
|
||||||
info->access.silent_deny();
|
info->access.silent_deny();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ROOT_ACCESS_APPS_ONLY:
|
case RootAccess::AppsOnly:
|
||||||
if (info->uid == AID_SHELL) {
|
if (info->uid == AID_SHELL) {
|
||||||
LOGW("Root access is disabled for ADB!\n");
|
LOGW("Root access is disabled for ADB!\n");
|
||||||
info->access.silent_deny();
|
info->access.silent_deny();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ROOT_ACCESS_APPS_AND_ADB:
|
case RootAccess::AppsAndAdb:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -395,19 +396,19 @@ void su_daemon_handler(int client, const sock_cred *cred) {
|
|||||||
if (ctx.req.target == -1)
|
if (ctx.req.target == -1)
|
||||||
ctx.req.target = ctx.pid;
|
ctx.req.target = ctx.pid;
|
||||||
else if (ctx.req.target == 0)
|
else if (ctx.req.target == 0)
|
||||||
ctx.info->cfg.mnt_ns = NAMESPACE_MODE_GLOBAL;
|
ctx.info->cfg.mnt_ns = MntNsMode::Global;
|
||||||
else if (ctx.info->cfg.mnt_ns == NAMESPACE_MODE_GLOBAL)
|
else if (ctx.info->cfg.mnt_ns == MntNsMode::Global)
|
||||||
ctx.info->cfg.mnt_ns = NAMESPACE_MODE_REQUESTER;
|
ctx.info->cfg.mnt_ns = MntNsMode::Requester;
|
||||||
switch (ctx.info->cfg.mnt_ns) {
|
switch (ctx.info->cfg.mnt_ns) {
|
||||||
case NAMESPACE_MODE_GLOBAL:
|
case MntNsMode::Global:
|
||||||
LOGD("su: use global namespace\n");
|
LOGD("su: use global namespace\n");
|
||||||
break;
|
break;
|
||||||
case NAMESPACE_MODE_REQUESTER:
|
case MntNsMode::Requester:
|
||||||
LOGD("su: use namespace of pid=[%d]\n", ctx.req.target);
|
LOGD("su: use namespace of pid=[%d]\n", ctx.req.target);
|
||||||
if (switch_mnt_ns(ctx.req.target))
|
if (switch_mnt_ns(ctx.req.target))
|
||||||
LOGD("su: setns failed, fallback to global\n");
|
LOGD("su: setns failed, fallback to global\n");
|
||||||
break;
|
break;
|
||||||
case NAMESPACE_MODE_ISOLATE:
|
case MntNsMode::Isolate:
|
||||||
LOGD("su: use new isolated namespace\n");
|
LOGD("su: use new isolated namespace\n");
|
||||||
switch_mnt_ns(ctx.req.target);
|
switch_mnt_ns(ctx.req.target);
|
||||||
xunshare(CLONE_NEWNS);
|
xunshare(CLONE_NEWNS);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user