From 71001ac8308d65f75f479cd4946e1d4d1789d428 Mon Sep 17 00:00:00 2001 From: Zorg Date: Fri, 26 Apr 2024 21:04:07 -0700 Subject: [PATCH] Extract archives in a separate directory from the input archive --- Autoupdate/AppInstaller.m | 17 +++++++++-- Autoupdate/SUBinaryDeltaUnarchiver.h | 2 +- Autoupdate/SUBinaryDeltaUnarchiver.m | 6 ++-- Autoupdate/SUDiskImageUnarchiver.h | 2 +- Autoupdate/SUDiskImageUnarchiver.m | 6 ++-- Autoupdate/SUFlatPackageUnarchiver.h | 2 +- Autoupdate/SUFlatPackageUnarchiver.m | 20 +++++++++++-- Autoupdate/SUInstaller.m | 41 +++++++++++++++++--------- Autoupdate/SUPipedUnarchiver.h | 2 +- Autoupdate/SUPipedUnarchiver.m | 6 ++-- Autoupdate/SUUnarchiver.h | 2 +- Autoupdate/SUUnarchiver.m | 12 ++++---- Tests/SUUnarchiverTest.swift | 17 ++++------- generate_appcast/Unarchive.swift | 43 +++++++--------------------- 14 files changed, 97 insertions(+), 81 deletions(-) diff --git a/Autoupdate/AppInstaller.m b/Autoupdate/AppInstaller.m index 9d0d037a51..9ae3d7226d 100644 --- a/Autoupdate/AppInstaller.m +++ b/Autoupdate/AppInstaller.m @@ -59,6 +59,7 @@ @implementation AppInstaller NSString *_userName; SUHost *_host; NSString *_updateDirectoryPath; + NSString *_extractionDirectory; NSString *_downloadName; NSString *_decryptionPassword; SUSignatures *_signatures; @@ -172,7 +173,8 @@ - (void)extractAndInstallUpdate SPU_OBJC_DIRECT [_communicator handleMessageWithIdentifier:SPUExtractionStarted data:[NSData data]]; NSString *archivePath = [_updateDirectoryPath stringByAppendingPathComponent:_downloadName]; - id unarchiver = [SUUnarchiver unarchiverForPath:archivePath updatingHostBundlePath:_host.bundlePath decryptionPassword:_decryptionPassword expectingInstallationType:_installationType]; + + id unarchiver = [SUUnarchiver unarchiverForPath:archivePath extractionDirectory:_extractionDirectory updatingHostBundlePath:_host.bundlePath decryptionPassword:_decryptionPassword expectingInstallationType:_installationType]; NSError *unarchiverError = nil; BOOL success = NO; @@ -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 }]]; @@ -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; @@ -453,6 +456,13 @@ - (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; @@ -460,6 +470,7 @@ - (void)handleMessageWithIdentifier:(int32_t)identifier data:(NSData *)data 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]; @@ -521,7 +532,7 @@ - (void)startInstallation SPU_OBJC_DIRECT dispatch_async(_installerQueue, ^{ NSError *installerError = nil; - id installer = [SUInstaller installerForHost:self->_host expectedInstallationType:self->_installationType updateDirectory:self->_updateDirectoryPath homeDirectory:self->_homeDirectory userName:self->_userName error:&installerError]; + id 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(), ^{ diff --git a/Autoupdate/SUBinaryDeltaUnarchiver.h b/Autoupdate/SUBinaryDeltaUnarchiver.h index db3a28868e..8a2bba902a 100644 --- a/Autoupdate/SUBinaryDeltaUnarchiver.h +++ b/Autoupdate/SUBinaryDeltaUnarchiver.h @@ -19,7 +19,7 @@ SPU_OBJC_DIRECT_MEMBERS #endif @interface SUBinaryDeltaUnarchiver : NSObject -- (instancetype)initWithArchivePath:(NSString *)archivePath updateHostBundlePath:(NSString *)updateHostBundlePath; +- (instancetype)initWithArchivePath:(NSString *)archivePath extractionDirectory:(NSString *)extractionDirectory updateHostBundlePath:(NSString *)updateHostBundlePath; + (BOOL)canUnarchivePath:(NSString *)path; diff --git a/Autoupdate/SUBinaryDeltaUnarchiver.m b/Autoupdate/SUBinaryDeltaUnarchiver.m index f9bdbc15f6..77871d4122 100644 --- a/Autoupdate/SUBinaryDeltaUnarchiver.m +++ b/Autoupdate/SUBinaryDeltaUnarchiver.m @@ -20,6 +20,7 @@ @implementation SUBinaryDeltaUnarchiver { NSString *_archivePath; NSString *_updateHostBundlePath; + NSString *_extractionDirectory; } + (BOOL)canUnarchivePath:(NSString *)path @@ -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; } @@ -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){ diff --git a/Autoupdate/SUDiskImageUnarchiver.h b/Autoupdate/SUDiskImageUnarchiver.h index 311983acfd..89081c0244 100644 --- a/Autoupdate/SUDiskImageUnarchiver.h +++ b/Autoupdate/SUDiskImageUnarchiver.h @@ -18,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN SPU_OBJC_DIRECT_MEMBERS @interface SUDiskImageUnarchiver : NSObject -- (instancetype)initWithArchivePath:(NSString *)archivePath decryptionPassword:(nullable NSString *)decryptionPassword; +- (instancetype)initWithArchivePath:(NSString *)archivePath extractionDirectory:(NSString *)extractionDirectory decryptionPassword:(nullable NSString *)decryptionPassword; + (BOOL)canUnarchivePath:(NSString *)path; diff --git a/Autoupdate/SUDiskImageUnarchiver.m b/Autoupdate/SUDiskImageUnarchiver.m index 8d97756c46..16cd1bc059 100644 --- a/Autoupdate/SUDiskImageUnarchiver.m +++ b/Autoupdate/SUDiskImageUnarchiver.m @@ -19,6 +19,7 @@ @implementation SUDiskImageUnarchiver { NSString *_archivePath; NSString *_decryptionPassword; + NSString *_extractionDirectory; } + (BOOL)canUnarchivePath:(NSString *)path @@ -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; } @@ -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)]; diff --git a/Autoupdate/SUFlatPackageUnarchiver.h b/Autoupdate/SUFlatPackageUnarchiver.h index 41afd29658..d1450652a2 100644 --- a/Autoupdate/SUFlatPackageUnarchiver.h +++ b/Autoupdate/SUFlatPackageUnarchiver.h @@ -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 -- (instancetype)initWithFlatPackagePath:(NSString *)flatPackagePath expectingInstallationType:(NSString *)installationType; +- (instancetype)initWithFlatPackagePath:(NSString *)flatPackagePath extractionDirectory:(NSString *)extractionDirectory expectingInstallationType:(NSString *)installationType; + (BOOL)canUnarchivePath:(NSString *)path; diff --git a/Autoupdate/SUFlatPackageUnarchiver.m b/Autoupdate/SUFlatPackageUnarchiver.m index 9259438a5d..88f1559277 100644 --- a/Autoupdate/SUFlatPackageUnarchiver.m +++ b/Autoupdate/SUFlatPackageUnarchiver.m @@ -20,6 +20,7 @@ @implementation SUFlatPackageUnarchiver { NSString *_flatPackagePath; NSString *_expectedInstallationType; + NSString *_extractionDirectory; } + (BOOL)canUnarchivePath:(NSString *)path @@ -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; } @@ -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:©Error]) { + 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]; + } } } diff --git a/Autoupdate/SUInstaller.m b/Autoupdate/SUInstaller.m index f76402a13e..1e48fead98 100644 --- a/Autoupdate/SUInstaller.m +++ b/Autoupdate/SUInstaller.m @@ -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 @@ -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]; @@ -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 diff --git a/Autoupdate/SUPipedUnarchiver.h b/Autoupdate/SUPipedUnarchiver.h index 47cec537fe..3d0277660d 100644 --- a/Autoupdate/SUPipedUnarchiver.h +++ b/Autoupdate/SUPipedUnarchiver.h @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN SPU_OBJC_DIRECT_MEMBERS @interface SUPipedUnarchiver : NSObject -- (instancetype)initWithArchivePath:(NSString *)archivePath; +- (instancetype)initWithArchivePath:(NSString *)archivePath extractionDirectory:(NSString *)extractionDirectory; + (BOOL)canUnarchivePath:(NSString *)path; diff --git a/Autoupdate/SUPipedUnarchiver.m b/Autoupdate/SUPipedUnarchiver.m index 5d0931fa50..ba55ea3715 100644 --- a/Autoupdate/SUPipedUnarchiver.m +++ b/Autoupdate/SUPipedUnarchiver.m @@ -17,6 +17,7 @@ @implementation SUPipedUnarchiver { NSString *_archivePath; + NSString *_extractionDirectory; } static NSArray * _Nullable _commandAndArgumentsConformingToTypeOfPath(NSString *path) @@ -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; } @@ -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); diff --git a/Autoupdate/SUUnarchiver.h b/Autoupdate/SUUnarchiver.h index d885a552dd..ced346823b 100644 --- a/Autoupdate/SUUnarchiver.h +++ b/Autoupdate/SUUnarchiver.h @@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN SPU_OBJC_DIRECT_MEMBERS @interface SUUnarchiver : NSObject -+ (nullable id )unarchiverForPath:(NSString *)path updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword expectingInstallationType:(NSString *)installationType; ++ (nullable id )unarchiverForPath:(NSString *)path extractionDirectory:(NSString *)extractionDirectory updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword expectingInstallationType:(NSString *)installationType; @end diff --git a/Autoupdate/SUUnarchiver.m b/Autoupdate/SUUnarchiver.m index 515c2f0a16..6268ff03ee 100644 --- a/Autoupdate/SUUnarchiver.m +++ b/Autoupdate/SUUnarchiver.m @@ -18,27 +18,25 @@ @implementation SUUnarchiver -+ (nullable id )unarchiverForPath:(NSString *)path updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword expectingInstallationType:(NSString *)installationType ++ (nullable id )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; diff --git a/Tests/SUUnarchiverTest.swift b/Tests/SUUnarchiverTest.swift index 183072fd97..5c3e08d37c 100644 --- a/Tests/SUUnarchiverTest.swift +++ b/Tests/SUUnarchiverTest.swift @@ -24,10 +24,9 @@ class SUUnarchiverTest: XCTestCase let unarchivedSuccessExpectation = super.expectation(description: "Unarchived Success (format: \(archiveExtension))") let unarchivedFailureExpectation = super.expectation(description: "Unarchived Failure (format: \(archiveExtension))") - let tempArchiveURL = tempDirectoryURL.appendingPathComponent(archiveResourceURL.lastPathComponent) let extractedAppURL = tempDirectoryURL.appendingPathComponent(extractedAppName).appendingPathExtension("app") - self.unarchiveTestAppWithExtension(archiveExtension, appName: appName, tempDirectoryURL: tempDirectoryURL, tempArchiveURL: tempArchiveURL, archiveResourceURL: archiveResourceURL, password: password, expectingInstallationType: installationType, expectingSuccess: expectingSuccess, testExpectation: unarchivedSuccessExpectation) + self.unarchiveTestAppWithExtension(archiveExtension, appName: appName, tempDirectoryURL: tempDirectoryURL, archiveResourceURL: archiveResourceURL, password: password, expectingInstallationType: installationType, expectingSuccess: expectingSuccess, testExpectation: unarchivedSuccessExpectation) self.unarchiveNonExistentFileTestFailureAppWithExtension(archiveExtension, tempDirectoryURL: tempDirectoryURL, password: password, expectingInstallationType: installationType, testExpectation: unarchivedFailureExpectation) super.waitForExpectations(timeout: 30.0, handler: nil) @@ -40,7 +39,7 @@ class SUUnarchiverTest: XCTestCase func unarchiveNonExistentFileTestFailureAppWithExtension(_ archiveExtension: String, tempDirectoryURL: URL, password: String?, expectingInstallationType installationType: String, testExpectation: XCTestExpectation) { let tempArchiveURL = tempDirectoryURL.appendingPathComponent("error-invalid").appendingPathExtension(archiveExtension) - let unarchiver = SUUnarchiver.unarchiver(forPath: tempArchiveURL.path, updatingHostBundlePath: nil, decryptionPassword: password, expectingInstallationType: installationType)! + let unarchiver = SUUnarchiver.unarchiver(forPath: tempArchiveURL.path, extractionDirectory: tempDirectoryURL.path, updatingHostBundlePath: nil, decryptionPassword: password, expectingInstallationType: installationType)! unarchiver.unarchive(completionBlock: {(error: Error?) -> Void in XCTAssertNotNil(error) @@ -49,13 +48,9 @@ class SUUnarchiverTest: XCTestCase } // swiftlint:disable function_parameter_count - func unarchiveTestAppWithExtension(_ archiveExtension: String, appName: String, tempDirectoryURL: URL, tempArchiveURL: URL, archiveResourceURL: URL, password: String?, expectingInstallationType installationType: String, expectingSuccess: Bool, testExpectation: XCTestExpectation) { - - let fileManager = FileManager.default - - try! fileManager.copyItem(at: archiveResourceURL, to: tempArchiveURL) - - let unarchiver = SUUnarchiver.unarchiver(forPath: tempArchiveURL.path, updatingHostBundlePath: nil, decryptionPassword: password, expectingInstallationType: installationType)! + func unarchiveTestAppWithExtension(_ archiveExtension: String, appName: String, tempDirectoryURL: URL, archiveResourceURL: URL, password: String?, expectingInstallationType installationType: String, expectingSuccess: Bool, testExpectation: XCTestExpectation) { + + let unarchiver = SUUnarchiver.unarchiver(forPath: archiveResourceURL.path, extractionDirectory: tempDirectoryURL.path, updatingHostBundlePath: nil, decryptionPassword: password, expectingInstallationType: installationType)! unarchiver.unarchive(completionBlock: {(error: Error?) -> Void in if expectingSuccess { @@ -133,7 +128,7 @@ class SUUnarchiverTest: XCTestCase #endif #if SPARKLE_BUILD_PACKAGE_SUPPORT - func testUnarchivingFlatPackage() + func testUnarchivingBarePackage() { self.unarchiveTestAppWithExtension("pkg", resourceName: "test", expectingInstallationType: SPUInstallationTypeGuidedPackage) diff --git a/generate_appcast/Unarchive.swift b/generate_appcast/Unarchive.swift index d3e95aff4a..44f8cfcdbf 100644 --- a/generate_appcast/Unarchive.swift +++ b/generate_appcast/Unarchive.swift @@ -6,40 +6,17 @@ import Foundation func unarchive(itemPath: URL, archiveDestDir: URL, callback: @escaping (Error?) -> Void) { - let fileManager = FileManager.default - let tempDir = archiveDestDir.appendingPathExtension("tmp") - let itemCopy = tempDir.appendingPathComponent(itemPath.lastPathComponent) - - _ = try? fileManager.createDirectory(at: tempDir, withIntermediateDirectories: true, attributes: [:]) - - do { - do { - try fileManager.linkItem(at: itemPath, to: itemCopy) - } catch { - try fileManager.copyItem(at: itemPath, to: itemCopy) - } - if let unarchiver = SUUnarchiver.unarchiver(forPath: itemCopy.path, updatingHostBundlePath: nil, decryptionPassword: nil, expectingInstallationType: SPUInstallationTypeApplication) { - unarchiver.unarchive(completionBlock: { (error: Error?) in - if error != nil { - callback(error) - return - } + if let unarchiver = SUUnarchiver.unarchiver(forPath: itemPath.path, extractionDirectory: archiveDestDir.path, updatingHostBundlePath: nil, decryptionPassword: nil, expectingInstallationType: SPUInstallationTypeApplication) { + unarchiver.unarchive(completionBlock: { (error: Error?) in + if error != nil { + callback(error) + return + } - _ = try? fileManager.removeItem(at: itemCopy) - do { - try fileManager.moveItem(at: tempDir, to: archiveDestDir) - callback(nil) - } catch { - callback(error) - } - }, progressBlock: nil) - } else { - _ = try? fileManager.removeItem(at: itemCopy) - callback(makeError(code: .unarchivingError, "Not a supported archive format: \(itemCopy)")) - } - } catch { - _ = try? fileManager.removeItem(at: tempDir) - callback(error) + callback(nil) + }, progressBlock: nil) + } else { + callback(makeError(code: .unarchivingError, "Not a supported archive format: \(itemPath)")) } }