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:
topjohnwu 2019-02-18 03:05:13 -05:00
parent 14aa6041ec
commit 692f893e1f
5 changed files with 78 additions and 117 deletions

View File

@ -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;
}

View File

@ -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 {

View File

@ -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");

View File

@ -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

View File

@ -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;
}