Monitor /data/system/packages.xml
Reinstalling system apps as data creates tons of issues. Calling pm path <pkg> is extremely expensive and doesn't work in post-fs-data. Parse through packages.xml to get APK path and UID at the same time. As a bonus, we don't need to traverse /data/app for packages anymore.
This commit is contained in:
parent
14aa6041ec
commit
692f893e1f
@ -20,7 +20,6 @@ using namespace std;
|
||||
// Protect access to both hide_list and hide_uid
|
||||
pthread_mutex_t list_lock;
|
||||
vector<string> hide_list;
|
||||
set<int> hide_uid;
|
||||
|
||||
// Treat GMS separately as we're only interested in one component
|
||||
int gms_uid = -1;
|
||||
@ -60,21 +59,6 @@ static void hide_sensitive_props() {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Bionic's atoi runs through strtol().
|
||||
* Use our own implementation for faster conversion.
|
||||
*/
|
||||
static inline int parse_int(const char *s) {
|
||||
int val = 0;
|
||||
char c;
|
||||
while ((c = *(s++))) {
|
||||
if (c > '9' || c < '0')
|
||||
return -1;
|
||||
val = val * 10 + c - '0';
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
// Leave /proc fd opened as we're going to read from it repeatedly
|
||||
static DIR *procfp;
|
||||
void crawl_procfs(const function<bool (int)> &fn) {
|
||||
@ -148,22 +132,12 @@ static void kill_process(int uid) {
|
||||
});
|
||||
}
|
||||
|
||||
static int add_pkg_uid(const char *pkg) {
|
||||
static int get_pkg_uid(const char *pkg) {
|
||||
char path[4096];
|
||||
struct stat st;
|
||||
const char *data = SDK_INT >= 24 ? "/data/user_de/0" : "/data/data";
|
||||
sprintf(path, "%s/%s", data, pkg);
|
||||
if (stat(path, &st) == 0) {
|
||||
hide_uid.insert(st.st_uid);
|
||||
return st.st_uid;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void refresh_uid() {
|
||||
hide_uid.clear();
|
||||
for (auto &s : hide_list)
|
||||
add_pkg_uid(s.c_str());
|
||||
return stat(path, &st) ? -1 : st.st_uid;
|
||||
}
|
||||
|
||||
void clean_magisk_props() {
|
||||
@ -192,7 +166,7 @@ int add_list(const char *pkg) {
|
||||
{
|
||||
MutexGuard lock(list_lock);
|
||||
hide_list.emplace_back(pkg);
|
||||
uid = add_pkg_uid(pkg);
|
||||
uid = get_pkg_uid(pkg);
|
||||
}
|
||||
|
||||
kill_process(uid);
|
||||
@ -235,17 +209,17 @@ int rm_list(int client) {
|
||||
int ret = rm_list(pkg);
|
||||
free(pkg);
|
||||
if (ret == DAEMON_SUCCESS)
|
||||
update_inotify_mask(true);
|
||||
update_inotify_mask();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_list(void *, int, char **data, char**) {
|
||||
LOGI("hide_list init: [%s]\n", *data);
|
||||
hide_list.emplace_back(*data);
|
||||
kill_process(*data);
|
||||
int uid = add_pkg_uid(*data);
|
||||
int uid = get_pkg_uid(*data);
|
||||
if (strcmp(*data, SAFETYNET_PKG) == 0)
|
||||
gms_uid = uid;
|
||||
kill_process(uid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ int rm_list(int client);
|
||||
void ls_list(int client);
|
||||
|
||||
// Update APK list for inotify
|
||||
void update_inotify_mask(bool refresh = false);
|
||||
void update_inotify_mask();
|
||||
|
||||
// Process monitor
|
||||
void proc_monitor();
|
||||
@ -34,7 +34,6 @@ void proc_monitor();
|
||||
// Utility functions
|
||||
void manage_selinux();
|
||||
void clean_magisk_props();
|
||||
void refresh_uid();
|
||||
void crawl_procfs(const std::function<bool (int)> &fn);
|
||||
|
||||
static inline int get_uid(const int pid) {
|
||||
@ -49,10 +48,24 @@ static inline int get_uid(const int pid) {
|
||||
return st.st_uid % 100000;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bionic's atoi runs through strtol().
|
||||
* Use our own implementation for faster conversion.
|
||||
*/
|
||||
static inline int parse_int(const char *s) {
|
||||
int val = 0;
|
||||
char c;
|
||||
while ((c = *(s++))) {
|
||||
if (c > '9' || c < '0')
|
||||
return -1;
|
||||
val = val * 10 + c - '0';
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
extern bool hide_enabled;
|
||||
extern pthread_mutex_t list_lock;
|
||||
extern std::vector<std::string> hide_list;
|
||||
extern std::set<int> hide_uid;
|
||||
extern int gms_uid;
|
||||
|
||||
enum {
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
#include <magisk.h>
|
||||
#include <utils.h>
|
||||
@ -32,6 +33,7 @@ using namespace std;
|
||||
extern char *system_block, *vendor_block, *data_block;
|
||||
|
||||
static int inotify_fd = -1;
|
||||
static set<int> hide_uid;
|
||||
|
||||
// Workaround for the lack of pthread_cancel
|
||||
static void term_thread(int) {
|
||||
@ -189,102 +191,67 @@ static int xinotify_add_watch(int fd, const char* path, uint32_t mask) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *append_path(char *eof, const char *name) {
|
||||
*(eof++) = '/';
|
||||
char c;
|
||||
while ((c = *(name++)))
|
||||
*(eof++) = c;
|
||||
*eof = '\0';
|
||||
return eof;
|
||||
}
|
||||
|
||||
#define DATA_APP "/data/app"
|
||||
static int new_inotify;
|
||||
static int data_app_wd;
|
||||
static vector<bool> app_in_data;
|
||||
static void find_apks(char *path, char *eof) {
|
||||
DIR *dir = opendir(path);
|
||||
if (dir == nullptr)
|
||||
return;
|
||||
static const string_view APK_EXT(".apk");
|
||||
|
||||
struct dirent *entry;
|
||||
char *dash;
|
||||
for (; (entry = xreaddir(dir)); *eof = '\0') {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
if (entry->d_type == DT_DIR) {
|
||||
find_apks(path, append_path(eof, entry->d_name));
|
||||
} else if (strend(entry->d_name, ".apk") == 0) {
|
||||
append_path(eof, entry->d_name);
|
||||
/* Supported path will be in either format:
|
||||
* /data/app/[pkg]-[hash or 1 or 2]/base.apk
|
||||
* /data/app/[pkg]-[1 or 2].apk */
|
||||
if ((dash = strchr(path, '-')) == nullptr)
|
||||
continue;
|
||||
*dash = '\0';
|
||||
for (int i = 0; i < hide_list.size(); ++i) {
|
||||
if (hide_list[i] == path + sizeof(DATA_APP)) {
|
||||
*dash = '-';
|
||||
append_path(eof, entry->d_name);
|
||||
xinotify_add_watch(new_inotify, path, IN_OPEN | IN_DELETE);
|
||||
app_in_data[i] = true;
|
||||
break;
|
||||
static bool parse_packages_xml(string_view &s) {
|
||||
if (!str_starts(s, "<package "))
|
||||
return true;
|
||||
/* <package key1="value1" key2="value2"....> */
|
||||
char *start = (char *) s.data();
|
||||
start[s.length() - 2] = '\0'; /* Remove trailing '>' */
|
||||
char key[32], value[1024];
|
||||
char *tok;
|
||||
start += 9; /* Skip '<package ' */
|
||||
while ((tok = strtok_r(nullptr, " ", &start))) {
|
||||
sscanf(tok, "%[^=]=\"%[^\"]", key, value);
|
||||
string_view value_view(value);
|
||||
if (strcmp(key, "name") == 0) {
|
||||
if (std::count(hide_list.begin(), hide_list.end(), value_view) == 0)
|
||||
return true;
|
||||
} else if (strcmp(key, "codePath") == 0) {
|
||||
if (ends_with(value_view, APK_EXT)) {
|
||||
// Directly add to inotify list
|
||||
xinotify_add_watch(new_inotify, value, IN_OPEN);
|
||||
} else {
|
||||
DIR *dir = opendir(value);
|
||||
if (dir == nullptr)
|
||||
return true;
|
||||
struct dirent *entry;
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (ends_with(entry->d_name, APK_EXT)) {
|
||||
strcpy(value + value_view.length(), "/");
|
||||
strcpy(value + value_view.length() + 1, entry->d_name);
|
||||
xinotify_add_watch(new_inotify, value, IN_OPEN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
*dash = '-';
|
||||
break;
|
||||
} else if (strcmp(key, "userId") == 0 || strcmp(key, "sharedUserId") == 0) {
|
||||
hide_uid.insert(parse_int(value));
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Iterate through /data/app and search all .apk files
|
||||
void update_inotify_mask(bool refresh) {
|
||||
char buf[4096];
|
||||
|
||||
void update_inotify_mask() {
|
||||
new_inotify = inotify_init();
|
||||
if (new_inotify < 0) {
|
||||
LOGE("proc_monitor: Cannot initialize inotify: %s\n", strerror(errno));
|
||||
term_thread(TERM_THREAD);
|
||||
}
|
||||
fcntl(new_inotify, F_SETFD, FD_CLOEXEC);
|
||||
|
||||
LOGD("proc_monitor: Updating inotify list\n");
|
||||
strcpy(buf, DATA_APP);
|
||||
app_in_data.clear();
|
||||
bool reinstall = false;
|
||||
{
|
||||
MutexGuard lock(list_lock);
|
||||
app_in_data.resize(hide_list.size(), false);
|
||||
find_apks(buf, buf + sizeof(DATA_APP) - 1);
|
||||
// Stop monitoring /data/app
|
||||
if (inotify_fd >= 0)
|
||||
inotify_rm_watch(inotify_fd, data_app_wd);
|
||||
// All apps on the hide list should be installed in data
|
||||
auto it = hide_list.begin();
|
||||
for (bool in_data : app_in_data) {
|
||||
if (!in_data) {
|
||||
if (reinstall_apk(it->c_str()) != 0) {
|
||||
// Reinstallation failed, remove from hide list
|
||||
hide_list.erase(it);
|
||||
refresh = true;
|
||||
continue;
|
||||
}
|
||||
reinstall = true;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
if (refresh && !reinstall)
|
||||
refresh_uid();
|
||||
}
|
||||
if (reinstall) {
|
||||
// Rerun detection
|
||||
close(new_inotify);
|
||||
update_inotify_mask(refresh);
|
||||
return;
|
||||
hide_uid.clear();
|
||||
file_readline("/data/system/packages.xml", parse_packages_xml, true);
|
||||
}
|
||||
|
||||
// Add /data/app itself to the watch list to detect app (un)installations/updates
|
||||
data_app_wd = xinotify_add_watch(new_inotify, DATA_APP, IN_CLOSE_WRITE | IN_MOVED_TO | IN_DELETE);
|
||||
// Add /data/system to monitor /data/system/packages.xml
|
||||
xinotify_add_watch(new_inotify, "/data/system", IN_CLOSE_WRITE);
|
||||
|
||||
int tmp = inotify_fd;
|
||||
inotify_fd = new_inotify;
|
||||
@ -306,20 +273,19 @@ void proc_monitor() {
|
||||
|
||||
// Read inotify events
|
||||
ssize_t len;
|
||||
char buf[4096];
|
||||
char buf[512];
|
||||
auto event = reinterpret_cast<inotify_event *>(buf);
|
||||
while ((len = read(inotify_fd, buf, sizeof(buf))) >= 0) {
|
||||
if (len < sizeof(*event))
|
||||
continue;
|
||||
|
||||
if (event->mask & IN_OPEN) {
|
||||
// Since we're just watching files,
|
||||
// extracting file name is not possible from querying event
|
||||
MutexGuard lock(list_lock);
|
||||
crawl_procfs(process_pid);
|
||||
} else if (!(event->mask & IN_IGNORED)) {
|
||||
LOGD("proc_monitor: inotify: /data/app change detected\n");
|
||||
update_inotify_mask(true);
|
||||
} else if ((event->mask & IN_CLOSE_WRITE) && strcmp(event->name, "packages.xml") == 0) {
|
||||
LOGD("proc_monitor: /data/system/packages.xml updated\n");
|
||||
update_inotify_mask();
|
||||
}
|
||||
}
|
||||
PLOGE("proc_monitor: read inotify");
|
||||
|
@ -201,6 +201,8 @@ int exec_command_sync(Args &&...args) {
|
||||
return exec_command_sync(exec, args...);
|
||||
}
|
||||
|
||||
bool ends_with(const std::string_view &s1, const std::string_view &s2);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -236,3 +236,9 @@ void set_nice_name(const char *name) {
|
||||
strlcpy(argv0, name, name_len);
|
||||
prctl(PR_SET_NAME, name);
|
||||
}
|
||||
|
||||
bool ends_with(const std::string_view &s1, const std::string_view &s2) {
|
||||
unsigned l1 = s1.length();
|
||||
unsigned l2 = s2.length();
|
||||
return l1 < l2 ? false : s1.compare(l1 - l2, l2, s2) == 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user