Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract archives in a separate directory from the input archive (2.2.x) #2553

Merged
merged 5 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Build & Tests

on:
push:
branches: [ 2.x, 2.2.2-dev, master ]
branches: [ 2.x, 2.2.x, master ]
pull_request:
branches: [ 2.x, 2.2.2-dev, master ]
branches: [ 2.x, 2.2.x, master ]

jobs:
build:
Expand Down
17 changes: 14 additions & 3 deletions Autoupdate/AppInstaller.m
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ @interface AppInstaller () <NSXPCListenerDelegate, SUInstallerCommunicationProto
@property (nonatomic, readonly) NSString *userName;
@property (nonatomic) SUHost *host;
@property (nonatomic, copy) NSString *updateDirectoryPath;
@property (nonatomic, copy) NSString *extractionDirectory;
@property (nonatomic, copy) NSString *downloadName;
@property (nonatomic, copy) NSString *decryptionPassword;
@property (nonatomic, strong) SUSignatures *signatures;
Expand Down Expand Up @@ -94,6 +95,7 @@ @implementation AppInstaller
@synthesize updateValidator = _updateValidator;
@synthesize host = _host;
@synthesize updateDirectoryPath = _updateDirectoryPath;
@synthesize extractionDirectory = _extractionDirectory;
@synthesize downloadName = _downloadName;
@synthesize decryptionPassword = _decryptionPassword;
@synthesize signatures = _signatures;
Expand Down Expand Up @@ -193,7 +195,7 @@ - (void)extractAndInstallUpdate
[self.communicator handleMessageWithIdentifier:SPUExtractionStarted data:[NSData data]];

NSString *archivePath = [self.updateDirectoryPath stringByAppendingPathComponent:self.downloadName];
id<SUUnarchiverProtocol> unarchiver = [SUUnarchiver unarchiverForPath:archivePath updatingHostBundlePath:self.host.bundlePath decryptionPassword:self.decryptionPassword expectingInstallationType:self.installationType];
id<SUUnarchiverProtocol> unarchiver = [SUUnarchiver unarchiverForPath:archivePath extractionDirectory:self.extractionDirectory updatingHostBundlePath:self.host.bundlePath decryptionPassword:self.decryptionPassword expectingInstallationType:self.installationType];

NSError *unarchiverError = nil;
BOOL success = NO;
Expand Down Expand Up @@ -226,7 +228,7 @@ - (void)extractAndInstallUpdate
[self.communicator handleMessageWithIdentifier:SPUValidationStarted data:[NSData data]];

NSError *validationError = nil;
BOOL validationSuccess = [self.updateValidator validateWithUpdateDirectory:self.updateDirectoryPath error:&validationError];
BOOL validationSuccess = [self.updateValidator validateWithUpdateDirectory:self.extractionDirectory error:&validationError];

if (!validationSuccess) {
[self cleanupAndExitWithStatus:EXIT_FAILURE error:[NSError errorWithDomain:SUSparkleErrorDomain code:SPUInstallerError userInfo:@{ NSLocalizedDescriptionKey: @"Update validation was a failure", NSUnderlyingErrorKey: validationError }]];
Expand Down Expand Up @@ -276,6 +278,7 @@ - (void)unarchiverDidFailWithError:(NSError *)error
// but may as well set other fields to nil too
[self clearUpdateDirectory];
self.downloadName = nil;
self.extractionDirectory = nil;
self.decryptionPassword = nil;
self.signatures = nil;
self.relaunchPath = nil;
Expand Down Expand Up @@ -407,13 +410,21 @@ - (void)handleMessageWithIdentifier:(int32_t)identifier data:(NSData *)data
return;
}

NSString *extractionDirectory = [SPULocalCacheDirectory createUniqueDirectoryInDirectory:cacheInstallationPath];
if (extractionDirectory == nil) {
[self cleanupAndExitWithStatus:EXIT_FAILURE error:[NSError errorWithDomain:SUSparkleErrorDomain code:SPUInstallerError userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Error: Failed to create installation extraction directory in %@", cacheInstallationPath] }]];

return;
}

// Carry these properities separately rather than using the SUInstallationInputData object
// Some of our properties may slightly differ than our input and we don't want to make the mistake of using one of those
self.installationType = installationType;
self.relaunchPath = installationData.relaunchPath;
self.downloadName = installationData.downloadName;
self.signatures = installationData.signatures;
self.updateDirectoryPath = cacheInstallationPath;
self.extractionDirectory = extractionDirectory;
self.host = [[SUHost alloc] initWithBundle:hostBundle];

[self extractAndInstallUpdate];
Expand Down Expand Up @@ -463,7 +474,7 @@ - (void)startInstallation

dispatch_async(self.installerQueue, ^{
NSError *installerError = nil;
id <SUInstallerProtocol> installer = [SUInstaller installerForHost:self.host expectedInstallationType:self.installationType updateDirectory:self.updateDirectoryPath homeDirectory:self.homeDirectory userName:self.userName error:&installerError];
id <SUInstallerProtocol> installer = [SUInstaller installerForHost:self.host expectedInstallationType:self.installationType updateDirectory:self.extractionDirectory homeDirectory:self.homeDirectory userName:self.userName error:&installerError];

if (installer == nil) {
dispatch_async(dispatch_get_main_queue(), ^{
Expand Down
2 changes: 1 addition & 1 deletion Autoupdate/SUBinaryDeltaUnarchiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN

@interface SUBinaryDeltaUnarchiver : NSObject <SUUnarchiverProtocol>

- (instancetype)initWithArchivePath:(NSString *)archivePath updateHostBundlePath:(NSString *)updateHostBundlePath;
- (instancetype)initWithArchivePath:(NSString *)archivePath extractionDirectory:(NSString *)extractionDirectory updateHostBundlePath:(NSString *)updateHostBundlePath;

@end

Expand Down
7 changes: 5 additions & 2 deletions Autoupdate/SUBinaryDeltaUnarchiver.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ @interface SUBinaryDeltaUnarchiver ()

@property (nonatomic, copy, readonly) NSString *archivePath;
@property (nonatomic, copy, readonly) NSString *updateHostBundlePath;
@property (nonatomic, copy, readonly) NSString *extractionDirectory;

@end

@implementation SUBinaryDeltaUnarchiver

@synthesize archivePath = _archivePath;
@synthesize updateHostBundlePath = _updateHostBundlePath;
@synthesize extractionDirectory = _extractionDirectory;

+ (BOOL)canUnarchivePath:(NSString *)path
{
Expand Down Expand Up @@ -73,12 +75,13 @@ + (void)updateSpotlightImportersAtBundlePath:(NSString *)targetPath
}
}

- (instancetype)initWithArchivePath:(NSString *)archivePath updateHostBundlePath:(NSString *)updateHostBundlePath
- (instancetype)initWithArchivePath:(NSString *)archivePath extractionDirectory:(NSString *)extractionDirectory updateHostBundlePath:(NSString *)updateHostBundlePath
{
self = [super init];
if (self != nil) {
_archivePath = [archivePath copy];
_updateHostBundlePath = [updateHostBundlePath copy];
_extractionDirectory = [extractionDirectory copy];
}
return self;
}
Expand All @@ -96,7 +99,7 @@ - (void)unarchiveWithCompletionBlock:(void (^)(NSError * _Nullable))completionBl
- (void)extractDeltaWithNotifier:(SUUnarchiverNotifier *)notifier
{
NSString *sourcePath = self.updateHostBundlePath;
NSString *targetPath = [[self.archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[sourcePath lastPathComponent]];
NSString *targetPath = [self.extractionDirectory stringByAppendingPathComponent:[sourcePath lastPathComponent]];

NSError *applyDiffError = nil;
BOOL success = applyBinaryDelta(sourcePath, targetPath, self.archivePath, NO, ^(double progress){
Expand Down
2 changes: 1 addition & 1 deletion Autoupdate/SUDiskImageUnarchiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN

@interface SUDiskImageUnarchiver : NSObject <SUUnarchiverProtocol>

- (instancetype)initWithArchivePath:(NSString *)archivePath decryptionPassword:(nullable NSString *)decryptionPassword;
- (instancetype)initWithArchivePath:(NSString *)archivePath extractionDirectory:(NSString *)extractionDirectory decryptionPassword:(nullable NSString *)decryptionPassword;

@end

Expand Down
7 changes: 5 additions & 2 deletions Autoupdate/SUDiskImageUnarchiver.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ @interface SUDiskImageUnarchiver ()

@property (nonatomic, copy, readonly) NSString *archivePath;
@property (nullable, nonatomic, copy, readonly) NSString *decryptionPassword;
@property (nonatomic, copy, readonly) NSString *extractionDirectory;

@end

@implementation SUDiskImageUnarchiver

@synthesize archivePath = _archivePath;
@synthesize decryptionPassword = _decryptionPassword;
@synthesize extractionDirectory = _extractionDirectory;

+ (BOOL)canUnarchivePath:(NSString *)path
{
Expand All @@ -35,12 +37,13 @@ + (BOOL)mustValidateBeforeExtraction
return NO;
}

- (instancetype)initWithArchivePath:(NSString *)archivePath decryptionPassword:(nullable NSString *)decryptionPassword
- (instancetype)initWithArchivePath:(NSString *)archivePath extractionDirectory:(NSString *)extractionDirectory decryptionPassword:(nullable NSString *)decryptionPassword
{
self = [super init];
if (self != nil) {
_archivePath = [archivePath copy];
_decryptionPassword = [decryptionPassword copy];
_extractionDirectory = [extractionDirectory copy];
}
return self;
}
Expand Down Expand Up @@ -189,7 +192,7 @@ - (void)extractDMGWithNotifier:(SUUnarchiverNotifier *)notifier
for (NSString *item in contents)
{
NSString *fromPath = [mountPoint stringByAppendingPathComponent:item];
NSString *toPath = [[self.archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:item];
NSString *toPath = [self.extractionDirectory stringByAppendingPathComponent:item];

itemsCopied += 1.0;
[notifier notifyProgress:0.5 + itemsCopied/(totalItems*2.0)];
Expand Down
2 changes: 1 addition & 1 deletion Autoupdate/SUFlatPackageUnarchiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
// An unarchiver for flat packages that doesn't really do any unarchiving
@interface SUFlatPackageUnarchiver : NSObject <SUUnarchiverProtocol>

- (instancetype)initWithFlatPackagePath:(NSString *)flatPackagePath expectingInstallationType:(NSString *)installationType;
- (instancetype)initWithFlatPackagePath:(NSString *)flatPackagePath extractionDirectory:(NSString *)extractionDirectory expectingInstallationType:(NSString *)installationType;

@end

Expand Down
21 changes: 18 additions & 3 deletions Autoupdate/SUFlatPackageUnarchiver.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ @interface SUFlatPackageUnarchiver ()

@property (nonatomic, readonly) NSString *flatPackagePath;
@property (nonatomic, readonly) NSString *expectedInstallationType;
@property (nonatomic, readonly) NSString *extractionDirectory;

@end

@implementation SUFlatPackageUnarchiver

@synthesize flatPackagePath = _flatPackagePath;
@synthesize expectedInstallationType = _expectedInstallationType;
@synthesize extractionDirectory = _extractionDirectory;

+ (BOOL)canUnarchivePath:(NSString *)path
{
Expand All @@ -36,12 +38,13 @@ + (BOOL)mustValidateBeforeExtraction
return YES;
}

- (instancetype)initWithFlatPackagePath:(NSString *)flatPackagePath expectingInstallationType:(NSString *)installationType
- (instancetype)initWithFlatPackagePath:(NSString *)flatPackagePath extractionDirectory:(NSString *)extractionDirectory expectingInstallationType:(NSString *)installationType
{
self = [super init];
if (self != nil) {
_flatPackagePath = [flatPackagePath copy];
_expectedInstallationType = [installationType copy];
_extractionDirectory = [extractionDirectory copy];
}
return self;
}
Expand All @@ -57,8 +60,20 @@ - (void)unarchiveWithCompletionBlock:(void (^)(NSError * _Nullable))completionBl
} else if (![[NSFileManager defaultManager] fileExistsAtPath:self.flatPackagePath isDirectory:&isDirectory] || isDirectory) {
[notifier notifyFailureWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUUnarchivingError userInfo:@{ NSLocalizedDescriptionKey:[NSString stringWithFormat:@"Flat package does not exist at %@", self.flatPackagePath]}]];
} else {
[notifier notifyProgress:1.0];
[notifier notifySuccess];
// Copying the flat package should be very fast, especially on APFS
NSError *copyError = nil;
if (![[NSFileManager defaultManager] copyItemAtPath:self.flatPackagePath toPath:[self.extractionDirectory stringByAppendingPathComponent:self.flatPackagePath.lastPathComponent] error:&copyError]) {
NSMutableDictionary *userInfoDictionary = [NSMutableDictionary dictionaryWithDictionary:@{ NSLocalizedDescriptionKey:[NSString stringWithFormat:@"Flat package (%@) cannot be copied to extraction directory (%@)", self.flatPackagePath, self.extractionDirectory]}];

if (copyError != nil) {
userInfoDictionary[NSUnderlyingErrorKey] = copyError;
}

[notifier notifyFailureWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUUnarchivingError userInfo:userInfoDictionary]];
} else {
[notifier notifyProgress:1.0];
[notifier notifySuccess];
}
}
}

Expand Down
41 changes: 28 additions & 13 deletions Autoupdate/SUInstaller.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,6 @@

@implementation SUInstaller

+ (BOOL)isAliasFolderAtPath:(NSString *)path
{
NSNumber *aliasFlag = nil;
[[NSURL fileURLWithPath:path] getResourceValue:&aliasFlag forKey:NSURLIsAliasFileKey error:nil];
NSNumber *directoryFlag = nil;
[[NSURL fileURLWithPath:path] getResourceValue:&directoryFlag forKey:NSURLIsDirectoryKey error:nil];
return aliasFlag.boolValue && directoryFlag.boolValue;
}

+ (nullable NSString *)installSourcePathInUpdateFolder:(NSString *)inUpdateFolder forHost:(SUHost *)host isPackage:(BOOL *)isPackagePtr isGuided:(BOOL *)isGuidedPtr
{
NSParameterAssert(inUpdateFolder);
Expand All @@ -49,6 +40,34 @@ + (nullable NSString *)installSourcePathInUpdateFolder:(NSString *)inUpdateFolde

while ((currentFile = [dirEnum nextObject])) {
NSString *currentPath = [inUpdateFolder stringByAppendingPathComponent:currentFile];

// Ignore all symbolic links and aliases
{
NSURL *currentPathURL = [NSURL fileURLWithPath:currentPath];

NSNumber *symbolicLinkFlag = nil;
[currentPathURL getResourceValue:&symbolicLinkFlag forKey:NSURLIsSymbolicLinkKey error:NULL];
if (symbolicLinkFlag.boolValue) {
// NSDirectoryEnumerator won't recurse into symlinked directories
continue;
}

NSNumber *aliasFlag = nil;
[currentPathURL getResourceValue:&aliasFlag forKey:NSURLIsAliasFileKey error:NULL];

if (aliasFlag.boolValue) {
NSNumber *directoryFlag = nil;
[currentPathURL getResourceValue:&directoryFlag forKey:NSURLIsDirectoryKey error:NULL];

// Some DMGs have symlinks into /Applications! That's no good!
if (directoryFlag.boolValue) {
[dirEnum skipDescendents];
}

continue;
}
}

NSString *currentFilename = [currentFile lastPathComponent];
NSString *currentExtension = [currentFile pathExtension];
NSString *currentFilenameNoExtension = [currentFilename stringByDeletingPathExtension];
Expand Down Expand Up @@ -78,10 +97,6 @@ + (nullable NSString *)installSourcePathInUpdateFolder:(NSString *)inUpdateFolde
break;
}
}

// Some DMGs have symlinks into /Applications! That's no good!
if ([self isAliasFolderAtPath:currentPath])
[dirEnum skipDescendents];
}

// We don't have a valid path. Try to use the fallback package.
Expand Down
2 changes: 1 addition & 1 deletion Autoupdate/SUPipedUnarchiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN

@interface SUPipedUnarchiver : NSObject <SUUnarchiverProtocol>

- (instancetype)initWithArchivePath:(NSString *)archivePath;
- (instancetype)initWithArchivePath:(NSString *)archivePath extractionDirectory:(NSString *)extractionDirectory;

@end

Expand Down
7 changes: 5 additions & 2 deletions Autoupdate/SUPipedUnarchiver.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
@interface SUPipedUnarchiver ()

@property (nonatomic, copy, readonly) NSString *archivePath;
@property (nonatomic, copy, readonly) NSString *extractionDirectory;

@end

@implementation SUPipedUnarchiver

@synthesize archivePath = _archivePath;
@synthesize extractionDirectory = _extractionDirectory;

+ (nullable NSArray <NSString *> *)commandAndArgumentsConformingToTypeOfPath:(NSString *)path
{
Expand Down Expand Up @@ -63,11 +65,12 @@ + (BOOL)mustValidateBeforeExtraction
return NO;
}

- (instancetype)initWithArchivePath:(NSString *)archivePath
- (instancetype)initWithArchivePath:(NSString *)archivePath extractionDirectory:(NSString *)extractionDirectory
{
self = [super init];
if (self != nil) {
_archivePath = [archivePath copy];
_extractionDirectory = [extractionDirectory copy];
}
return self;
}
Expand All @@ -93,7 +96,7 @@ - (void)extractArchivePipingDataToCommand:(NSString *)command arguments:(NSArray
{
// *** GETS CALLED ON NON-MAIN THREAD!!!
@autoreleasepool {
NSString *destination = [self.archivePath stringByDeletingLastPathComponent];
NSString *destination = self.extractionDirectory;

SULog(SULogLevelDefault, @"Extracting using '%@' '%@' < '%@' '%@'", command, [args componentsJoinedByString:@"' '"], self.archivePath, destination);

Expand Down
2 changes: 1 addition & 1 deletion Autoupdate/SUUnarchiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN

@interface SUUnarchiver : NSObject

+ (nullable id <SUUnarchiverProtocol>)unarchiverForPath:(NSString *)path updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword expectingInstallationType:(NSString *)installationType;
+ (nullable id <SUUnarchiverProtocol>)unarchiverForPath:(NSString *)path extractionDirectory:(NSString *)extractionDirectory updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword expectingInstallationType:(NSString *)installationType;

@end

Expand Down
10 changes: 5 additions & 5 deletions Autoupdate/SUUnarchiver.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,21 @@

@implementation SUUnarchiver

+ (nullable id <SUUnarchiverProtocol>)unarchiverForPath:(NSString *)path updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword expectingInstallationType:(NSString *)installationType
+ (nullable id <SUUnarchiverProtocol>)unarchiverForPath:(NSString *)path extractionDirectory:(NSString *)extractionDirectory updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword expectingInstallationType:(NSString *)installationType
{
if ([SUPipedUnarchiver canUnarchivePath:path]) {
return [[SUPipedUnarchiver alloc] initWithArchivePath:path];
return [[SUPipedUnarchiver alloc] initWithArchivePath:path extractionDirectory:extractionDirectory];

} else if ([SUDiskImageUnarchiver canUnarchivePath:path]) {
return [[SUDiskImageUnarchiver alloc] initWithArchivePath:path decryptionPassword:decryptionPassword];
return [[SUDiskImageUnarchiver alloc] initWithArchivePath:path extractionDirectory:extractionDirectory decryptionPassword:decryptionPassword];

} else if ([SUBinaryDeltaUnarchiver canUnarchivePath:path]) {
assert(hostPath != nil);
NSString *nonNullHostPath = hostPath;
return [[SUBinaryDeltaUnarchiver alloc] initWithArchivePath:path updateHostBundlePath:nonNullHostPath];
return [[SUBinaryDeltaUnarchiver alloc] initWithArchivePath:path extractionDirectory:extractionDirectory updateHostBundlePath:nonNullHostPath];
} else if ([SUFlatPackageUnarchiver canUnarchivePath:path]) {
// Flat packages are only supported for guided packaage installs
return [[SUFlatPackageUnarchiver alloc] initWithFlatPackagePath:path expectingInstallationType:installationType];
return [[SUFlatPackageUnarchiver alloc] initWithFlatPackagePath:path extractionDirectory:extractionDirectory expectingInstallationType:installationType];
}
return nil;
}
Expand Down
Loading
Loading