App manager: database names, data sizes, faster discovery

This commit is contained in:
admin committed 2026-11-11 15:46:27 +08:00
1 parent 168a89cb8a
commit 0e5851c521
6 files changed
+1280 -65

No files matched your search

+100 -16
View File
@@ -12,6 +12,7 @@
#import "worse_query.h"
#import "pathleak.h"
#import "fallback_query.h"
#import "WQLSStore.h"
#import <Foundation/Foundation.h>
#import <Security/Security.h>
@@ -88,9 +89,10 @@ static NSInteger WQOSMajor(void)
static BOOL WQUsesAlternateFlags(NSString *path)
{
// worse_query needs the alternate flags for App Group containers on 26.x.
return WQOSMajor() == 26 &&
[path containsString:@"/var/mobile/Containers/Shared/AppGroup"];
// iOS 26 needs the alternative query flags for app, extension, daemon
// and app-group containers. iOS 27 normally uses the standard flags.
(void)path;
return WQOSMajor() < 27;
}
/// Paths documented by forcequitOS/worse_query, ordered from broadest to most
@@ -177,22 +179,55 @@ NSString *WQBestAppGroup(void)
#pragma mark - Query engine
static BOOL WQGrantActuallyOpens(NSString *target)
{
int descriptor = open(target.fileSystemRepresentation,
O_RDONLY | O_CLOEXEC);
if (descriptor >= 0) {
close(descriptor);
return YES;
}
// An ordinary permission error is not a failed grant. Only EPERM means
// the sandbox extension did not help.
return errno != EPERM;
}
static int64_t WQQueryOnce(NSString *target, BOOL alternate,
BOOL createIfMissing)
{
NSString *group = WQBestAppGroup();
if (!group.length) return -1;
int64_t handle = worse_query(target, group, alternate, createIfMissing);
if (handle < 0)
handle = fallback_query(target, alternate);
return handle;
}
static int64_t WQAcquireExtension(NSString *target, BOOL createIfMissing)
{
if (![target hasPrefix:@"/"]) return -255;
NSString *group = WQBestAppGroup();
if (!group.length) return -1;
if (!WQBestAppGroup().length) return -1;
[gQueryLock lock];
int64_t handle = worse_query(target, group,
WQUsesAlternateFlags(target), createIfMissing);
if (handle < 0) {
int64_t first = handle;
handle = fallback_query(target, WQUsesAlternateFlags(target));
if (handle < 0 && getenv("WQ_VERBOSE")) {
NSLog(@"[WQ] grant failed path=%@ worse_query=%lld fallback=%lld",
target, (long long)first, (long long)handle);
BOOL alternate = WQUsesAlternateFlags(target);
int64_t handle = WQQueryOnce(target, alternate, createIfMissing);
int64_t first = handle;
if (handle >= 0 && !createIfMissing &&
!WQGrantActuallyOpens(target)) {
worse_query_release(handle);
handle = -8;
}
if (handle < 0 && handle != -1 && handle != -2) {
int64_t retry = WQQueryOnce(target, !alternate, createIfMissing);
if (retry >= 0 && !createIfMissing && !WQGrantActuallyOpens(target)) {
worse_query_release(retry);
retry = -8;
}
if (retry >= 0) handle = retry;
}
if (handle < 0 && getenv("WQ_VERBOSE")) {
NSLog(@"[WQ] grant failed path=%@ first=%lld final=%lld",
target, (long long)first, (long long)handle);
}
[gQueryLock unlock];
return handle;
@@ -616,6 +651,8 @@ static NSString *WQInferIdentifierFromDataDirectory(NSString *containerPath)
static NSString *WQContainerIdentifier(NSString *containerPath)
{
if (!containerPath.length) return nil;
NSString *lsIdentifier = WQLSBundleIDForContainer(containerPath);
if (lsIdentifier.length) return lsIdentifier;
NSString *metadataPath = [containerPath
stringByAppendingPathComponent:@".com.apple.mobile_container_manager.metadata.plist"];
NSFileManager *fm = NSFileManager.defaultManager;
@@ -771,9 +808,12 @@ WQAppInfoFromContainers(NSArray<NSString *> *containers)
} mutableCopy];
if (!dataMap[bundleID]) dataMap[bundleID] = containerPath;
NSString *bundlePath = gBundleMap[bundleID];
NSString *bundlePath = WQLSBundlePathForIdentifier(bundleID);
if (!bundlePath.length) bundlePath = gBundleMap[bundleID];
if (bundlePath.length) {
info[@"bundlePath"] = bundlePath;
if (!WQGrantExtraPath(bundlePath, NO))
WQGrantExtraPath(bundlePath.stringByDeletingLastPathComponent, NO);
NSDictionary *infoPlist = [NSDictionary dictionaryWithContentsOfFile:
[bundlePath stringByAppendingPathComponent:@"Info.plist"]];
NSString *name = infoPlist[@"CFBundleDisplayName"];
@@ -783,6 +823,8 @@ WQAppInfoFromContainers(NSArray<NSString *> *containers)
if (!version.length) version = infoPlist[@"CFBundleVersion"];
if (version.length) info[@"version"] = version;
}
NSString *lsName = WQLSDisplayName(bundleID);
if (lsName.length) info[@"name"] = lsName;
Class proxyClass = NSClassFromString(@"LSApplicationProxy");
id proxy = [proxyClass respondsToSelector:
@@ -797,6 +839,24 @@ WQAppInfoFromContainers(NSArray<NSString *> *containers)
(![info[@"name"] isKindOfClass:NSString.class] ||
[info[@"name"] isEqualToString:bundleID]))
info[@"name"] = proxyName;
if (getenv("WQ_APP_NAME_DEBUG")) {
static int debugNames;
if (debugNames++ < 40) {
NSString *infoName = nil;
NSInteger infoKeys = 0;
NSString *debugPath = info[@"bundlePath"];
if (debugPath.length) {
NSDictionary *plist = [NSDictionary dictionaryWithContentsOfFile:
[debugPath stringByAppendingPathComponent:@"Info.plist"]];
infoKeys = plist.count;
infoName = plist[@"CFBundleDisplayName"] ?:
plist[@"CFBundleName"];
}
NSLog(@"[WQ][name-debug] %@ ls=%@ path=%@ plistKeys=%ld plistName=%@",
bundleID, WQLSDisplayName(bundleID) ?: @"(nil)",
debugPath ?: @"(nil)", (long)infoKeys, infoName ?: @"(nil)");
}
}
NSString *proxyVersion = [proxy respondsToSelector:NSSelectorFromString(@"bundleVersion")]
? ((id(*)(id, SEL))objc_msgSend)(proxy,
NSSelectorFromString(@"bundleVersion")) : nil;
@@ -1070,8 +1130,30 @@ static NSArray<NSDictionary<NSString *, NSString *> *> *WQBuildAppList(void)
// even though each child container is accepted. Always enumerate children
// and request their extensions individually.
WQGrantRoot(root, @"App Data");
WQPostStartupProgress(0.26, @"正在扫描应用数据容器…");
NSArray<NSString *> *containers = WQEnumerateContainers(root);
WQPostStartupProgress(0.20, @"正在读取 LaunchServices 应用数据库…");
WQLoadLaunchServicesDatabase();
NSMutableArray<NSString *> *lsContainers = [NSMutableArray array];
NSString *lsPrefix = [root stringByAppendingString:@"/"];
for (NSString *path in WQLSContainerPaths() ?: @[]) {
NSString *canonical = WQCanonicalPath(path);
if (![canonical hasPrefix:lsPrefix]) continue;
if (canonical.length != lsPrefix.length + 36) continue;
if (![[NSUUID alloc] initWithUUIDString:
[canonical substringFromIndex:lsPrefix.length]]) continue;
[lsContainers addObject:canonical];
}
NSArray<NSString *> *containers = nil;
if (lsContainers.count >= 2) {
containers = lsContainers;
WQPostStartupProgress(0.30, [NSString stringWithFormat:
@"应用数据库已列出 %lu 个数据容器,正在授权…",
(unsigned long)containers.count]);
} else {
WQPostStartupProgress(0.26, @"正在扫描应用数据容器…");
containers = WQEnumerateContainers(root);
}
WQPostStartupProgress(0.40, [NSString stringWithFormat:
@"发现 %lu 个数据容器,正在逐个授权…", (unsigned long)containers.count]);
NSUInteger granted = 0;
@@ -1229,6 +1311,8 @@ static void WQBuildBundleMapIfNeeded(void)
NSString *WQBundlePathForIdentifier(NSString *bundleID)
{
if (!bundleID.length) return nil;
NSString *lsPath = WQLSBundlePathForIdentifier(bundleID);
if (lsPath.length) return lsPath;
if (getenv("WQ_BUNDLE_SCAN")) WQBuildBundleMapIfNeeded();
NSString *mapped = gBundleMap[bundleID];
if (mapped.length) return mapped;