From 24650eefe44e69c46798b8df50d34db68f6caf17 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 3 Jan 2025 11:38:15 -0800 Subject: [PATCH] Bind SQLite to Rust --- native/src/Android.mk | 1 - native/src/Cargo.lock | 1 + native/src/base/cstr.rs | 5 + native/src/core/Cargo.toml | 1 + native/src/core/bootstages.cpp | 43 ++-- native/src/core/daemon.cpp | 4 +- native/src/core/daemon.rs | 52 ++++- native/src/core/db.cpp | 300 ------------------------- native/src/core/db.rs | 337 +++++++++++++++++++++++++++++ native/src/core/deny/utils.cpp | 10 +- native/src/core/include/db.hpp | 128 ----------- native/src/core/include/sqlite.hpp | 52 ++++- native/src/core/lib.rs | 80 ++++++- native/src/core/package.cpp | 8 +- native/src/core/sqlite.cpp | 211 +++++++++++++++++- native/src/core/su/su.hpp | 4 +- native/src/core/su/su_daemon.cpp | 49 +++-- 17 files changed, 765 insertions(+), 521 deletions(-) delete mode 100644 native/src/core/db.cpp create mode 100644 native/src/core/db.rs delete mode 100644 native/src/core/include/db.hpp diff --git a/native/src/Android.mk b/native/src/Android.mk index a569f58fd..3ed0ca82d 100644 --- a/native/src/Android.mk +++ b/native/src/Android.mk @@ -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 \ diff --git a/native/src/Cargo.lock b/native/src/Cargo.lock index 6506b71d4..d08543eeb 100644 --- a/native/src/Cargo.lock +++ b/native/src/Cargo.lock @@ -449,6 +449,7 @@ dependencies = [ "num-traits", "pb-rs", "quick-protobuf", + "thiserror", ] [[package]] diff --git a/native/src/base/cstr.rs b/native/src/base/cstr.rs index 5b7ea90ab..11eb04d5f 100644 --- a/native/src/base/cstr.rs +++ b/native/src/base/cstr.rs @@ -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 diff --git a/native/src/core/Cargo.toml b/native/src/core/Cargo.toml index f92d5bb3c..f1f7284d3 100644 --- a/native/src/core/Cargo.toml +++ b/native/src/core/Cargo.toml @@ -18,3 +18,4 @@ num-traits = { workspace = true } num-derive = { workspace = true } quick-protobuf = { workspace = true } bytemuck = { workspace = true, features = ["derive"] } +thiserror = { workspace = true } diff --git a/native/src/core/bootstages.cpp b/native/src/core/bootstages.cpp index 8b265a7ef..320180545 100644 --- a/native/src/core/bootstages.cpp +++ b/native/src/core/bootstages.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include @@ -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) diff --git a/native/src/core/daemon.cpp b/native/src/core/daemon.cpp index d4a997dc2..69f08c670 100644 --- a/native/src/core/daemon.cpp +++ b/native/src/core/daemon.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include 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); diff --git a/native/src/core/daemon.rs b/native/src/core/daemon.rs index 5737c2f9a..a6d21d1f2 100644 --- a/native/src/core/daemon.rs +++ b/native/src/core/daemon.rs @@ -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>, + pub sql_connection: Mutex>, boot_stage_lock: Mutex, 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; + fn ipc_read_string(&mut self) -> io::Result; +} + +impl IpcRead for T { + fn ipc_read_int(&mut self) -> io::Result { + let mut val: i32 = 0; + self.read_pod(&mut val)?; + Ok(val) + } + + fn ipc_read_string(&mut self) -> io::Result { + 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 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()) + } +} diff --git a/native/src/core/db.cpp b/native/src/core/db.cpp deleted file mode 100644 index 8a57d4070..000000000 --- a/native/src/core/db.cpp +++ /dev/null @@ -1,300 +0,0 @@ -#include -#include - -#include -#include -#include -#include -#include - -#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 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(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; - - 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(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(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; -} diff --git a/native/src/core/db.rs b/native/src/core/db.rs new file mode 100644 index 000000000..ccce97746 --- /dev/null +++ b/native/src/core/db.rs @@ -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 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); +unsafe impl Send for Sqlite3 {} + +type SqlBindCallback = Option) -> i32>; +type SqlExecCallback = Option; + +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( + 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 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(&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::), + 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, + ) + }) +} diff --git a/native/src/core/deny/utils.cpp b/native/src/core/deny/utils.cpp index 3a907aa1c..1996b4ab4 100644 --- a/native/src/core/deny/utils.cpp +++ b/native/src/core/deny/utils.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #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(); } } diff --git a/native/src/core/include/db.hpp b/native/src/core/include/db.hpp deleted file mode 100644 index 244b8a7f6..000000000 --- a/native/src/core/include/db.hpp +++ /dev/null @@ -1,128 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include -#include - -/*************** - * 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; - -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 list) : args(list), curr(0) {} - int operator()(int index, DbStatement &stmt); - bool empty() const { return args.empty(); } -private: - std::vector 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 -concept DbData = requires(T t, StringSlice s, DbValues &v) { t(s, v); }; - -template -bool db_exec(const char *sql, DbArgs args, T &data) { - return db_exec(sql, std::move(args), (db_exec_callback) std::ref(data)); -} diff --git a/native/src/core/include/sqlite.hpp b/native/src/core/include/sqlite.hpp index 8eec3338c..8b25cadc6 100644 --- a/native/src/core/include/sqlite.hpp +++ b/native/src/core/include/sqlite.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #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; 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; + +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 list) : args(list), curr(0) {} + int operator()(int index, DbStatement &stmt); + bool empty() const { return args.empty(); } +private: + std::vector args; + size_t curr; +}; + +bool db_exec(const char *sql, DbArgs args = {}, db_exec_callback exec_fn = {}); + +template +concept DbData = requires(T t, StringSlice s, DbValues &v) { t(s, v); }; + +template +bool db_exec(const char *sql, DbArgs args, T &data) { + return db_exec(sql, std::move(args), (db_exec_callback) std::ref(data)); +} diff --git a/native/src/core/lib.rs b/native/src/core/lib.rs index c01d26b5f..2a45dbcba 100644 --- a/native/src/core/lib.rs +++ b/native/src/core/lib.rs @@ -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; } diff --git a/native/src/core/package.cpp b/native/src/core/package.cpp index 2d35b5222..da84bcf1e 100644 --- a/native/src/core/package.cpp +++ b/native/src/core/package.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include 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; } } diff --git a/native/src/core/sqlite.cpp b/native/src/core/sqlite.cpp index 926a954a4..0949f2dc5 100644 --- a/native/src/core/sqlite.cpp +++ b/native/src/core/sqlite.cpp @@ -1,16 +1,22 @@ #include +#include #include #include 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 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(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(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(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 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(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; + + 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(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(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; +} diff --git a/native/src/core/su/su.hpp b/native/src/core/su/su.hpp index 1cd92d60a..b3c6aa6bb 100644 --- a/native/src/core/su/su.hpp +++ b/native/src/core/su/su.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #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; diff --git a/native/src/core/su/su_daemon.cpp b/native/src/core/su/su_daemon.cpp index 2e966f2aa..ec2493142 100644 --- a/native/src/core/su/su_daemon.cpp +++ b/native/src/core/su/su_daemon.cpp @@ -18,7 +18,8 @@ static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER; static shared_ptr 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 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);