[Sandbox] Refine broker to return handle to token

Sandbox Broker now returns a handle to the exact path object that was
retained by the caller, so it will be released correctly, regardless of
what happens to the list of bookmarked paths.

Also refined the bookmark path comparison function. For existing paths,
it will find the first match. For new paths, it will prefer the longest
path instead, to try to find the deepest matching bookmark.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
swiftingly
Christopher Snowhill 2022-06-20 22:10:43 -07:00
parent dbdcad6c04
commit 8dddf6a115
10 changed files with 98 additions and 102 deletions

View File

@ -48,7 +48,7 @@ static NSString *g_make_unpack_path(NSString *archive, NSString *file, NSString
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker"); id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker]; id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker beginFolderAccess:url]; const void *sbHandle = [sandboxBroker beginFolderAccess:url];
fex_t *fex; fex_t *fex;
fex_err_t error = fex_open(&fex, [[url path] UTF8String]); fex_err_t error = fex_open(&fex, [[url path] UTF8String]);
@ -68,7 +68,7 @@ static NSString *g_make_unpack_path(NSString *archive, NSString *file, NSString
fex_close(fex); fex_close(fex);
[sandboxBroker endFolderAccess:url]; [sandboxBroker endFolderAccess:sbHandle];
return files; return files;
} }

View File

@ -20,7 +20,8 @@
NSUInteger size; NSUInteger size;
NSURL *_url; NSURL *_url;
NSURL *fileURL;
const void *sbHandle;
} }
@end @end

View File

@ -85,12 +85,10 @@ static BOOL g_parse_unpack_path(NSString *src, NSString **archive, NSString **fi
if(![type isEqualToString:@"fex"]) if(![type isEqualToString:@"fex"])
return NO; return NO;
fileURL = [NSURL fileURLWithPath:archive];
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker"); id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker]; id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker beginFolderAccess:fileURL]; sbHandle = [sandboxBroker beginFolderAccess:[NSURL fileURLWithPath:archive]];
fex_err_t error; fex_err_t error;
@ -161,11 +159,12 @@ static BOOL g_parse_unpack_path(NSString *src, NSString **archive, NSString **fi
fex = NULL; fex = NULL;
} }
if(fileURL) { if(sbHandle) {
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker"); id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker]; id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker endFolderAccess:fileURL]; [sandboxBroker endFolderAccess:sbHandle];
sbHandle = NULL;
} }
} }

View File

@ -21,6 +21,8 @@
FILE *_fd; FILE *_fd;
NSURL *_url; NSURL *_url;
const void *sbHandle;
} }
@end @end

View File

@ -31,7 +31,7 @@
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker"); id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker]; id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker beginFolderAccess:url]; sbHandle = [sandboxBroker beginFolderAccess:url];
NSString *path = [url path]; NSString *path = [url path];
@ -131,10 +131,13 @@
fex = NULL; fex = NULL;
} }
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker"); if(sbHandle) {
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker]; id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker endFolderAccess:_url]; [sandboxBroker endFolderAccess:sbHandle];
sbHandle = NULL;
}
} }
- (NSURL *)url { - (NSURL *)url {

View File

@ -24,7 +24,7 @@ class BMPlayer;
MIDIPlayer* player; MIDIPlayer* player;
midi_container midi_file; midi_container midi_file;
NSURL* sandboxURL; const void* sbHandle;
NSString* globalSoundFontPath; NSString* globalSoundFontPath;
BOOL soundFontsAssigned; BOOL soundFontsAssigned;

View File

@ -168,12 +168,14 @@ static OSType getOSType(const char *in_) {
globalSoundFontPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"soundFontPath"]; globalSoundFontPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"soundFontPath"];
if(globalSoundFontPath && [globalSoundFontPath length] > 0) { if(globalSoundFontPath && [globalSoundFontPath length] > 0) {
sandboxURL = [NSURL fileURLWithPath:[globalSoundFontPath stringByDeletingLastPathComponent]]; NSURL *sandboxURL = [NSURL fileURLWithPath:[globalSoundFontPath stringByDeletingLastPathComponent]];
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker"); id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker]; id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker beginFolderAccess:sandboxURL]; sbHandle = [sandboxBroker beginFolderAccess:sandboxURL];
} else {
sbHandle = NULL;
} }
// First detect if soundfont has gone AWOL // First detect if soundfont has gone AWOL
@ -345,13 +347,12 @@ static OSType getOSType(const char *in_) {
delete player; delete player;
player = NULL; player = NULL;
if(sandboxURL) { if(sbHandle) {
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker"); id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker]; id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker endFolderAccess:sandboxURL]; [sandboxBroker endFolderAccess:sbHandle];
sbHandle = NULL;
sandboxURL = nil;
} }
} }

View File

@ -31,7 +31,7 @@
id sandboxBrokerClass = NSClassFromString(@"SandboxBroker"); id sandboxBrokerClass = NSClassFromString(@"SandboxBroker");
id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker]; id sandboxBroker = [sandboxBrokerClass sharedSandboxBroker];
[sandboxBroker beginFolderAccess:url]; const void *sbHandle = [sandboxBroker beginFolderAccess:url];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
@ -237,7 +237,7 @@
} }
} }
[sandboxBroker endFolderAccess:url]; [sandboxBroker endFolderAccess:sbHandle];
return dict; return dict;
} }

View File

@ -18,8 +18,8 @@ NS_ASSUME_NONNULL_BEGIN
- (id)init; - (id)init;
- (void)shutdown; - (void)shutdown;
- (void)beginFolderAccess:(NSURL *)fileUrl; - (const void *)beginFolderAccess:(NSURL *)fileUrl;
- (void)endFolderAccess:(NSURL *)fileUrl; - (void)endFolderAccess:(const void *)handle;
@end @end

View File

@ -125,112 +125,102 @@ static NSURL *urlWithoutFragment(NSURL *u) {
} }
} }
- (void)recursivePathTest:(NSURL *)url removing:(BOOL)removing { + (BOOL)isPath:(NSURL *)path aSubdirectoryOf:(NSURL *)directory {
NSArray *pathComponents = [url pathComponents]; NSArray *pathComponents = [path pathComponents];
NSArray *directoryComponents = [directory pathComponents];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path" ascending:NO]; if([pathComponents count] < [directoryComponents count])
return NO;
for(size_t i = [pathComponents count]; i > 0; --i) { for(size_t i = 0; i < [directoryComponents count]; ++i) {
NSArray *partialComponents = [pathComponents subarrayWithRange:NSMakeRange(0, i)]; if(![pathComponents[i] isEqualToString:directoryComponents[i]])
NSURL *partialUrl = [NSURL fileURLWithPathComponents:partialComponents]; return NO;
NSString *matchString = [[partialUrl path] stringByAppendingString:@"*"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"path like %@", matchString];
NSArray *matchingObjects = [storage filteredArrayUsingPredicate:predicate];
if(matchingObjects && [matchingObjects count] > 0) {
if([matchingObjects count] > 1) {
matchingObjects = [matchingObjects sortedArrayUsingDescriptors:@[sortDescriptor]];
}
for(SandboxEntry *entry in matchingObjects) {
if([entry.path isEqualToString:[partialUrl path]]) {
if(!removing) {
entry.refCount += 1;
return;
} else {
if(entry.refCount > 1) {
entry.refCount -= 1;
return;
} else {
if(entry.secureUrl) {
[entry.secureUrl stopAccessingSecurityScopedResource];
entry.secureUrl = nil;
}
entry.refCount = 0;
[storage removeObject:entry];
return;
}
}
}
}
}
} }
if(removing) return; return YES;
}
- (SandboxEntry *)recursivePathTest:(NSURL *)url {
for(SandboxEntry *entry in storage) {
if([SandboxBroker isPath:url aSubdirectoryOf:[NSURL fileURLWithPath:entry.path]]) {
entry.refCount += 1;
return entry;
}
}
NSPersistentContainer *pc = [NSApp sharedPersistentContainer]; NSPersistentContainer *pc = [NSApp sharedPersistentContainer];
for(size_t i = [pathComponents count]; i > 0; --i) { NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"path.length" ascending:NO];
NSArray *partialComponents = [pathComponents subarrayWithRange:NSMakeRange(0, i)];
NSURL *partialUrl = [NSURL fileURLWithPathComponents:partialComponents];
NSString *matchString = [[partialUrl path] stringByAppendingString:@"*"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"path like %@", matchString]; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"];
request.sortDescriptors = @[sortDescriptor];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"SandboxToken"]; NSError *error = nil;
request.predicate = predicate; NSArray *results = [pc.viewContext executeFetchRequest:request error:&error];
request.sortDescriptors = @[sortDescriptor];
NSError *error = nil; if(results && [results count] > 0) {
NSArray *results = [pc.viewContext executeFetchRequest:request error:&error]; for(SandboxToken *token in results) {
if([SandboxBroker isPath:url aSubdirectoryOf:[NSURL fileURLWithPath:token.path]]) {
SandboxEntry *entry = [[SandboxEntry alloc] initWithToken:token];
if(results && [results count] > 0) { [storage addObject:entry];
for(SandboxToken *token in results) {
if([token.path isEqualToString:[partialUrl path]]) {
SandboxEntry *entry = [[SandboxEntry alloc] initWithToken:token];
[storage addObject:entry]; BOOL isStale;
NSError *err = nil;
BOOL isStale; NSURL *secureUrl = [NSURL URLByResolvingBookmarkData:token.bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&err];
NSError *err = nil; if(!secureUrl && err) {
NSURL *secureUrl = [NSURL URLByResolvingBookmarkData:token.bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&err]; ALog(@"Failed to access bookmark for URL: %@, error: %@", token.path, [err localizedDescription]);
if(!secureUrl && err) { return nil;
ALog(@"Failed to access bookmark for URL: %@, error: %@", token.path, [err localizedDescription]);
return;
}
entry.secureUrl = secureUrl;
[secureUrl startAccessingSecurityScopedResource];
return;
} }
entry.secureUrl = secureUrl;
[secureUrl startAccessingSecurityScopedResource];
return entry;
} }
} }
} }
return; return nil;
} }
- (void)beginFolderAccess:(NSURL *)fileUrl { - (const void *)beginFolderAccess:(NSURL *)fileUrl {
NSURL *folderUrl = [urlWithoutFragment(fileUrl) URLByDeletingLastPathComponent]; NSURL *folderUrl = [urlWithoutFragment(fileUrl) URLByDeletingLastPathComponent];
if(![folderUrl isFileURL]) return; if(![folderUrl isFileURL]) return NULL;
if(![NSApp respondsToSelector:@selector(sharedPersistentContainer)]) return; if(![NSApp respondsToSelector:@selector(sharedPersistentContainer)]) return NULL;
SandboxEntry *entry;
@synchronized(self) { @synchronized(self) {
[self recursivePathTest:folderUrl removing:NO]; entry = [self recursivePathTest:folderUrl];
}
if(entry) {
return CFBridgingRetain(entry);
} else {
return NULL;
} }
} }
- (void)endFolderAccess:(NSURL *)fileUrl { - (void)endFolderAccess:(const void *)handle {
NSURL *folderUrl = [urlWithoutFragment(fileUrl) URLByDeletingLastPathComponent]; if(!handle) return;
if(![folderUrl isFileURL]) return; SandboxEntry *entry = CFBridgingRelease(handle);
if(!entry) return;
@synchronized(self) { @synchronized(self) {
[self recursivePathTest:folderUrl removing:YES]; if(entry.refCount > 1) {
entry.refCount -= 1;
return;
} else {
if(entry.secureUrl) {
[entry.secureUrl stopAccessingSecurityScopedResource];
entry.secureUrl = nil;
}
entry.refCount = 0;
[storage removeObject:entry];
}
} }
} }