mirror of https://github.com/electron/electron
129 lines
7.2 KiB
Diff
129 lines
7.2 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Samuel Attard <samuel.r.attard@gmail.com>
|
|
Date: Mon, 28 Mar 2022 02:43:18 -0700
|
|
Subject: feat: add new Squirrel.Mac bundle installation method behind flag
|
|
|
|
The 'SquirrelMacEnableDirectContentsWrite' user default in your apps defaults suite
|
|
can be used to control this new installation method. It is designed to remove the
|
|
requirement that the updating process have write access to its parent directory.
|
|
|
|
With this feature enabled the updating process only needs write access to it's own
|
|
.app bundle folder, not the owning /Applications folder. This should allow more
|
|
non-admin users to update applications when appropriately granted group based permissions.
|
|
|
|
E.g. 775 and chown :staff
|
|
|
|
diff --git a/Squirrel/SQRLInstaller.m b/Squirrel/SQRLInstaller.m
|
|
index 7dd98ddee4ae0f4e01fd7aaa3486083bff7d0da1..c1f328fa8c3689218ef260347cb8f9d30b789efe 100644
|
|
--- a/Squirrel/SQRLInstaller.m
|
|
+++ b/Squirrel/SQRLInstaller.m
|
|
@@ -249,6 +249,7 @@ - (RACSignal *)acquireTargetBundleURLForRequest:(SQRLShipItRequest *)request {
|
|
] reduce:^(NSURL *directoryURL, SQRLCodeSignature *codeSignature) {
|
|
NSURL *targetBundleURL = request.targetBundleURL;
|
|
NSURL *newBundleURL = [directoryURL URLByAppendingPathComponent:targetBundleURL.lastPathComponent];
|
|
+ [NSFileManager.defaultManager createDirectoryAtURL:newBundleURL withIntermediateDirectories:FALSE attributes:nil error:nil];
|
|
|
|
return [[SQRLInstallerOwnedBundle alloc] initWithOriginalURL:request.targetBundleURL temporaryURL:newBundleURL codeSignature:codeSignature];
|
|
}]
|
|
@@ -481,10 +482,50 @@ - (RACSignal *)installItemToURL:(NSURL *)targetURL fromURL:(NSURL *)sourceURL {
|
|
NSParameterAssert(targetURL != nil);
|
|
NSParameterAssert(sourceURL != nil);
|
|
|
|
+ NSLog(@"Moving bundle from %@ to %@", sourceURL, targetURL);
|
|
+
|
|
+ // If both the sourceURL and the targetURL exist we can try to skip a permissions check
|
|
+ // by moving Thing.app/Contents directly. This allows us to update applications without
|
|
+ // permission to write files into the parent directory of Thing.app
|
|
+ //
|
|
+ // There is no known case where these directories don't exist but in order to handle
|
|
+ // edge cases / race conditions we'll handle it anyway.
|
|
+ //
|
|
+ // This exists check is non-atomic with the rename call below but that's OK
|
|
+ BOOL canRenameContentsDirectly = FALSE;
|
|
+ // For now while this is tested at scale this new option is behind a user default, this
|
|
+ // can be set by applications wishing to test this feature at runtime. If it causes issues
|
|
+ // it can be opted out by individual users by setting this key to false explicitly.
|
|
+ // Once this has bene tested at scale it will become the default for all Squirrel.Mac
|
|
+ // users.
|
|
+ NSUserDefaults *defaults = [[NSUserDefaults alloc] init];
|
|
+ [defaults addSuiteNamed:_applicationIdentifier];
|
|
+ // In cases where this code is being executed under the ShipIt executable it's running
|
|
+ // under an application identifier equal to {parent_identifier}.ShipIt
|
|
+ // In this case we need to use the true parent identifier too as that is 99% of the time
|
|
+ // where the key will be set.
|
|
+ if ([_applicationIdentifier hasSuffix:@".ShipIt"]) {
|
|
+ [defaults addSuiteNamed:[_applicationIdentifier substringToIndex:[_applicationIdentifier length] - 7]];
|
|
+ }
|
|
+
|
|
+ if ([defaults boolForKey:@"SquirrelMacEnableDirectContentsWrite"]) {
|
|
+ canRenameContentsDirectly = [NSFileManager.defaultManager fileExistsAtPath:targetURL.path] && [NSFileManager.defaultManager fileExistsAtPath:sourceURL.path];
|
|
+
|
|
+ if (canRenameContentsDirectly) {
|
|
+ NSLog(@"Moving bundles via 'Contents' folder rename");
|
|
+ } else {
|
|
+ NSLog(@"Moving bundles directly as one of source / target does not exist. This is unexpected.");
|
|
+ }
|
|
+ } else {
|
|
+ NSLog(@"Moving bundles directly as SquirrelMacEnableDirectContentsWrite is disabled for app: %@", _applicationIdentifier);
|
|
+ }
|
|
+ NSURL *targetContentsURL = canRenameContentsDirectly ? [targetURL URLByAppendingPathComponent:@"Contents"] : targetURL;
|
|
+ NSURL *sourceContentsURL = canRenameContentsDirectly ? [sourceURL URLByAppendingPathComponent:@"Contents"] : sourceURL;
|
|
+
|
|
return [[[[RACSignal
|
|
defer:^{
|
|
// rename() is atomic, NSFileManager sucks.
|
|
- if (rename(sourceURL.path.fileSystemRepresentation, targetURL.path.fileSystemRepresentation) == 0) {
|
|
+ if (rename(sourceContentsURL.path.fileSystemRepresentation, targetContentsURL.path.fileSystemRepresentation) == 0) {
|
|
return [RACSignal empty];
|
|
} else {
|
|
int code = errno;
|
|
@@ -497,24 +538,24 @@ - (RACSignal *)installItemToURL:(NSURL *)targetURL fromURL:(NSURL *)sourceURL {
|
|
}
|
|
}]
|
|
doCompleted:^{
|
|
- NSLog(@"Moved bundle from %@ to %@", sourceURL, targetURL);
|
|
+ NSLog(@"Moved bundle contents from %@ to %@", sourceContentsURL, targetContentsURL);
|
|
}]
|
|
catch:^(NSError *error) {
|
|
if (![error.domain isEqual:NSPOSIXErrorDomain] || error.code != EXDEV) return [RACSignal error:error];
|
|
|
|
// If the locations lie on two different volumes, remove the
|
|
// destination by hand, then perform a move.
|
|
- [NSFileManager.defaultManager removeItemAtURL:targetURL error:NULL];
|
|
+ [NSFileManager.defaultManager removeItemAtURL:targetContentsURL error:NULL];
|
|
|
|
- if ([NSFileManager.defaultManager moveItemAtURL:sourceURL toURL:targetURL error:&error]) {
|
|
- NSLog(@"Moved bundle across volumes from %@ to %@", sourceURL, targetURL);
|
|
+ if ([NSFileManager.defaultManager moveItemAtURL:sourceContentsURL toURL:targetContentsURL error:&error]) {
|
|
+ NSLog(@"Moved bundle contents across volumes from %@ to %@", sourceContentsURL, targetContentsURL);
|
|
return [RACSignal empty];
|
|
} else {
|
|
- NSString *description = [NSString stringWithFormat:NSLocalizedString(@"Couldn't move bundle %@ across volumes to %@", nil), sourceURL, targetURL];
|
|
+ NSString *description = [NSString stringWithFormat:NSLocalizedString(@"Couldn't move bundle contents %@ across volumes to %@", nil), sourceContentsURL, targetContentsURL];
|
|
return [RACSignal error:[self errorByAddingDescription:description code:SQRLInstallerErrorMovingAcrossVolumes toError:error]];
|
|
}
|
|
}]
|
|
- setNameWithFormat:@"%@ -installItemAtURL: %@ fromURL: %@", self, targetURL, sourceURL];
|
|
+ setNameWithFormat:@"%@ -installItemAtURL: %@ fromURL: %@", self, targetContentsURL, sourceContentsURL];
|
|
}
|
|
|
|
#pragma mark Quarantine Bit Removal
|
|
diff --git a/Squirrel/SQRLUpdater.m b/Squirrel/SQRLUpdater.m
|
|
index c81c820d61da3c7d1cfd2c516147c954a5773a0c..4c703159a2bb0239b7d4e1793a985b5ec2edcfa9 100644
|
|
--- a/Squirrel/SQRLUpdater.m
|
|
+++ b/Squirrel/SQRLUpdater.m
|
|
@@ -329,7 +329,12 @@ - (id)initWithUpdateRequest:(NSURLRequest *)updateRequest requestForDownload:(SQ
|
|
|
|
BOOL targetWritable = [self canWriteToURL:targetURL];
|
|
BOOL parentWritable = [self canWriteToURL:targetURL.URLByDeletingLastPathComponent];
|
|
- return [SQRLShipItLauncher launchPrivileged:!targetWritable || !parentWritable];
|
|
+ BOOL launchPrivileged = !targetWritable || !parentWritable;
|
|
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"SquirrelMacEnableDirectContentsWrite"]) {
|
|
+ // If SquirrelMacEnableDirectContentsWrite is enabled we don't care if the parent directory is writeable or not
|
|
+ BOOL launchPrivileged = !targetWritable;
|
|
+ }
|
|
+ return [SQRLShipItLauncher launchPrivileged:launchPrivileged];
|
|
}]
|
|
replayLazily]
|
|
setNameWithFormat:@"shipItLauncher"];
|