Skip to content

Commit

Permalink
Extract archives in a separate directory from the input archive (#2550)
Browse files Browse the repository at this point in the history
  • Loading branch information
zorgiepoo authored Apr 30, 2024
1 parent 5264c01 commit 31b462f
Show file tree
Hide file tree
Showing 14 changed files with 97 additions and 81 deletions.
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

0 comments on commit 31b462f

Please sign in to comment.