App manager: database names, data sizes, faster discovery
This commit is contained in:
1 parent
168a89cb8a
commit
0e5851c521
6 files changed
+1280
-65
No files matched your search
+100
-16
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user