mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-06-13 13:47:44 +02:00
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:
@ -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");
|
||||
|
Reference in New Issue
Block a user