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 #2550

Merged
merged 1 commit into from
Apr 30, 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
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 @@ @implementation AppInstaller
NSString *_userName;
SUHost *_host;
NSString *_updateDirectoryPath;
NSString *_extractionDirectory;
NSString *_downloadName;
NSString *_decryptionPassword;
SUSignatures *_signatures;
Expand Down Expand Up @@ -172,7 +173,8 @@ - (void)extractAndInstallUpdate SPU_OBJC_DIRECT
[_communicator handleMessageWithIdentifier:SPUExtractionStarted data:[NSData data]];

NSString *archivePath = [_updateDirectoryPath stringByAppendingPathComponent:_downloadName];
id<SUUnarchiverProtocol> unarchiver = [SUUnarchiver unarchiverForPath:archivePath updatingHostBundlePath:_host.bundlePath decryptionPassword:_decryptionPassword expectingInstallationType:_installationType];

id<SUUnarchiverProtocol> unarchiver = [SUUnarchiver unarchiverForPath:archivePath extractionDirectory:_extractionDirectory updatingHostBundlePath:_host.bundlePath decryptionPassword:_decryptionPassword expectingInstallationType:_installationType];

NSError *unarchiverError = nil;
BOOL success = NO;
Expand Down Expand Up @@ -213,7 +215,7 @@ - (void)extractAndInstallUpdate SPU_OBJC_DIRECT
[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 @@ -263,6 +265,7 @@ - (void)unarchiverDidFailWithError:(NSError *)error SPU_OBJC_DIRECT
// but may as well set other fields to nil too
[self clearUpdateDirectory];
_downloadName = nil;
_extractionDirectory = nil;
_decryptionPassword = nil;
_signatures = nil;
_relaunchPath = nil;
Expand Down Expand Up @@ -453,13 +456,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 properties 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 = downloadName;
self->_signatures = installationData.signatures;
self->_updateDirectoryPath = cacheInstallationPath;
self->_extractionDirectory = extractionDirectory;
self->_host = [[SUHost alloc] initWithBundle:hostBundle];
self->_verifierInformation = [[SPUVerifierInformation alloc] initWithExpectedVersion:installationData.expectedVersion expectedContentLength:installationData.expectedContentLength];

Expand Down Expand Up @@ -521,7 +532,7 @@ - (void)startInstallation SPU_OBJC_DIRECT

dispatch_async(_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 @@ -19,7 +19,7 @@ SPU_OBJC_DIRECT_MEMBERS
#endif
@interface SUBinaryDeltaUnarchiver : NSObject <SUUnarchiverProtocol>

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

+ (BOOL)canUnarchivePath:(NSString *)path;

Expand Down
6 changes: 4 additions & 2 deletions Autoupdate/SUBinaryDeltaUnarchiver.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ @implementation SUBinaryDeltaUnarchiver
{
NSString *_archivePath;
NSString *_updateHostBundlePath;
NSString *_extractionDirectory;
}

+ (BOOL)canUnarchivePath:(NSString *)path
Expand Down Expand Up @@ -71,12 +72,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 @@ -94,7 +96,7 @@ - (void)unarchiveWithCompletionBlock:(void (^)(NSError * _Nullable))completionBl
- (void)extractDeltaWithNotifier:(SUUnarchiverNotifier *)notifier
{
NSString *sourcePath = _updateHostBundlePath;
NSString *targetPath = [[_archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[sourcePath lastPathComponent]];
NSString *targetPath = [_extractionDirectory stringByAppendingPathComponent:[sourcePath lastPathComponent]];

NSError *applyDiffError = nil;
BOOL success = applyBinaryDelta(sourcePath, targetPath, _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 @@ -18,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN

SPU_OBJC_DIRECT_MEMBERS @interface SUDiskImageUnarchiver : NSObject <SUUnarchiverProtocol>

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

+ (BOOL)canUnarchivePath:(NSString *)path;

Expand Down
6 changes: 4 additions & 2 deletions Autoupdate/SUDiskImageUnarchiver.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ @implementation SUDiskImageUnarchiver
{
NSString *_archivePath;
NSString *_decryptionPassword;
NSString *_extractionDirectory;
}

+ (BOOL)canUnarchivePath:(NSString *)path
Expand All @@ -31,12 +32,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 @@ -171,7 +173,7 @@ - (void)extractDMGWithNotifier:(SUUnarchiverNotifier *)notifier SPU_OBJC_DIRECT
for (NSString *item in contents)
{
NSString *fromPath = [mountPoint stringByAppendingPathComponent:item];
NSString *toPath = [[_archivePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:item];
NSString *toPath = [_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 @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
// An unarchiver for flat packages that doesn't really do any unarchiving
SPU_OBJC_DIRECT_MEMBERS @interface SUFlatPackageUnarchiver : NSObject <SUUnarchiverProtocol>

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

+ (BOOL)canUnarchivePath:(NSString *)path;

Expand Down
20 changes: 17 additions & 3 deletions Autoupdate/SUFlatPackageUnarchiver.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ @implementation SUFlatPackageUnarchiver
{
NSString *_flatPackagePath;
NSString *_expectedInstallationType;
NSString *_extractionDirectory;
}

+ (BOOL)canUnarchivePath:(NSString *)path
Expand All @@ -32,12 +33,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 @@ -53,8 +55,20 @@ - (void)unarchiveWithCompletionBlock:(void (^)(NSError * _Nullable))completionBl
} else if (![[NSFileManager defaultManager] fileExistsAtPath:_flatPackagePath isDirectory:&isDirectory] || isDirectory) {
[notifier notifyFailureWithError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUUnarchivingError userInfo:@{ NSLocalizedDescriptionKey:[NSString stringWithFormat:@"Flat package does not exist at %@", _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:_flatPackagePath toPath:[_extractionDirectory stringByAppendingPathComponent:_flatPackagePath.lastPathComponent] error:&copyError]) {
NSMutableDictionary *userInfoDictionary = [NSMutableDictionary dictionaryWithDictionary:@{ NSLocalizedDescriptionKey:[NSString stringWithFormat:@"Flat package (%@) cannot be copied to extraction directory (%@)", _flatPackagePath, _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 SPU_OBJC_DIRECT
{
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
#if SPARKLE_BUILD_PACKAGE_SUPPORT
isPackage:(BOOL *)isPackagePtr isGuided:(BOOL *)isGuidedPtr
Expand All @@ -55,6 +46,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];
#if SPARKLE_BUILD_PACKAGE_SUPPORT
NSString *currentExtension = [currentFile pathExtension];
Expand Down Expand Up @@ -92,10 +111,6 @@ + (nullable NSString *)installSourcePathInUpdateFolder:(NSString *)inUpdateFolde
break;
}
}

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

#if SPARKLE_BUILD_PACKAGE_SUPPORT
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

SPU_OBJC_DIRECT_MEMBERS @interface SUPipedUnarchiver : NSObject <SUUnarchiverProtocol>

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

+ (BOOL)canUnarchivePath:(NSString *)path;

Expand Down
6 changes: 4 additions & 2 deletions Autoupdate/SUPipedUnarchiver.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
@implementation SUPipedUnarchiver
{
NSString *_archivePath;
NSString *_extractionDirectory;
}

static NSArray <NSString *> * _Nullable _commandAndArgumentsConformingToTypeOfPath(NSString *path)
Expand Down Expand Up @@ -59,11 +60,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 @@ -89,7 +91,7 @@ - (void)extractArchivePipingDataToCommand:(NSString *)command arguments:(NSArray
{
// *** GETS CALLED ON NON-MAIN THREAD!!!
@autoreleasepool {
NSString *destination = [_archivePath stringByDeletingLastPathComponent];
NSString *destination = _extractionDirectory;

SULog(SULogLevelDefault, @"Extracting using '%@' '%@' < '%@' '%@'", command, [args componentsJoinedByString:@"' '"], _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

SPU_OBJC_DIRECT_MEMBERS @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
12 changes: 5 additions & 7 deletions Autoupdate/SUUnarchiver.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,25 @@

@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];
}
#if SPARKLE_BUILD_DMG_SUPPORT
else if ([SUDiskImageUnarchiver canUnarchivePath:path]) {
return [[SUDiskImageUnarchiver alloc] initWithArchivePath:path decryptionPassword:decryptionPassword];

return [[SUDiskImageUnarchiver alloc] initWithArchivePath:path extractionDirectory:extractionDirectory decryptionPassword:decryptionPassword];
}
#endif
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];
}
#if SPARKLE_BUILD_PACKAGE_SUPPORT
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];
}
#endif
return nil;
Expand Down
Loading
Loading