From bbe887e160f9996fce56f1a82dc3364c969601a8 Mon Sep 17 00:00:00 2001 From: Zorg Date: Thu, 2 May 2024 16:11:13 +0200 Subject: [PATCH] Extract archives in a separate directory from the input archive This is ported from the 2.x branch. --- Sparkle/SUBasicUpdateDriver.m | 39 ++++++++++++++++++--------- Sparkle/SUBinaryDeltaUnarchiver.h | 2 +- Sparkle/SUBinaryDeltaUnarchiver.m | 7 +++-- Sparkle/SUDiskImageUnarchiver.h | 2 +- Sparkle/SUDiskImageUnarchiver.m | 7 +++-- Sparkle/SUFlatPackageUnarchiver.h | 2 +- Sparkle/SUFlatPackageUnarchiver.m | 21 ++++++++++++--- Sparkle/SUInstaller.m | 41 +++++++++++++++++++--------- Sparkle/SUPipedUnarchiver.h | 2 +- Sparkle/SUPipedUnarchiver.m | 7 +++-- Sparkle/SUUnarchiver.h | 2 +- Sparkle/SUUnarchiver.m | 11 ++++---- Tests/SUUnarchiverTest.swift | 14 +++------- generate_appcast/Unarchive.swift | 45 ++++++++----------------------- 14 files changed, 112 insertions(+), 90 deletions(-) diff --git a/Sparkle/SUBasicUpdateDriver.m b/Sparkle/SUBasicUpdateDriver.m index e4e357e124..2df221079b 100644 --- a/Sparkle/SUBasicUpdateDriver.m +++ b/Sparkle/SUBasicUpdateDriver.m @@ -30,6 +30,7 @@ #import "SPUURLRequest.h" #import "SPUDownloaderSession.h" +#import "SPULocalCacheDirectory.h" @interface SUBasicUpdateDriver () @@ -38,6 +39,7 @@ @interface SUBasicUpdateDriver () @property (assign) NSComparisonResult latestAppcastItemComparisonResult; @property (strong) SPUDownloader *download; @property (copy) NSString *downloadPath; +@property (copy) NSString *extractionDirectory; @property (strong) SUAppcastItem *nonDeltaUpdateItem; @property (copy) NSString *tempDir; @@ -54,6 +56,7 @@ @implementation SUBasicUpdateDriver @synthesize latestAppcastItemComparisonResult; @synthesize download; @synthesize downloadPath; +@synthesize extractionDirectory = _extractionDirectory; @synthesize nonDeltaUpdateItem; @synthesize tempDir; @@ -383,6 +386,7 @@ - (void)downloaderDidSetDestinationName:(NSString *)destinationName temporaryDir { self.tempDir = temporaryDirectory; self.downloadPath = [temporaryDirectory stringByAppendingPathComponent:destinationName]; + self.extractionDirectory = [SPULocalCacheDirectory createUniqueDirectoryInDirectory:temporaryDirectory]; } - (void)downloaderDidReceiveExpectedContentLength:(int64_t)__unused expectedContentLength @@ -441,22 +445,30 @@ - (void)downloaderDidFailWithError:(NSError *)error - (void)extractUpdate { - id updater = self.updater; - id unarchiver = [SUUnarchiver unarchiverForPath:self.downloadPath updatingHostBundlePath:self.host.bundlePath decryptionPassword:updater.decryptionPassword]; - BOOL success = NO; - if (!unarchiver) { - SULog(SULogLevelError, @"Error: No valid unarchiver for %@!", self.downloadPath); + + id updater = self.updater; + + id unarchiver; + if (self.extractionDirectory == nil) { + SULog(SULogLevelError, @"Error: Failed to create temporary extraction directory for %@!", self.downloadPath); + unarchiver = nil; } else { - self.updateValidator = [[SUUpdateValidator alloc] initWithDownloadPath:self.downloadPath signatures:self.updateItem.signatures host:self.host]; + unarchiver = [SUUnarchiver unarchiverForPath:self.downloadPath extractionDirectory:self.extractionDirectory updatingHostBundlePath:self.host.bundlePath decryptionPassword:updater.decryptionPassword]; + + if (!unarchiver) { + SULog(SULogLevelError, @"Error: No valid unarchiver for %@!", self.downloadPath); + } else { + self.updateValidator = [[SUUpdateValidator alloc] initWithDownloadPath:self.downloadPath signatures:self.updateItem.signatures host:self.host]; - // Currently unsafe archives are the only case where we can prevalidate before extraction, but that could change in the future - BOOL needsPrevalidation = [[unarchiver class] mustValidateBeforeExtraction]; + // Currently unsafe archives are the only case where we can prevalidate before extraction, but that could change in the future + BOOL needsPrevalidation = [[unarchiver class] mustValidateBeforeExtraction]; - if (needsPrevalidation) { - success = [self.updateValidator validateDownloadPath]; - } else { - success = YES; + if (needsPrevalidation) { + success = [self.updateValidator validateDownloadPath]; + } else { + success = YES; + } } } @@ -560,8 +572,9 @@ - (void)installWithToolAndRelaunch:(BOOL)relaunch displayingUserInterface:(BOOL) { assert(self.updateItem); assert(self.updateValidator); + assert(self.extractionDirectory); - BOOL validationCheckSuccess = [self.updateValidator validateWithUpdateDirectory:self.tempDir]; + BOOL validationCheckSuccess = [self.updateValidator validateWithUpdateDirectory:self.extractionDirectory]; if (!validationCheckSuccess) { NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: SULocalizedString(@"An error occurred while extracting the archive. Please try again later.", nil), diff --git a/Sparkle/SUBinaryDeltaUnarchiver.h b/Sparkle/SUBinaryDeltaUnarchiver.h index ba79a0476e..9ffe721d31 100644 --- a/Sparkle/SUBinaryDeltaUnarchiver.h +++ b/Sparkle/SUBinaryDeltaUnarchiver.h @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN @interface SUBinaryDeltaUnarchiver : NSObject -- (instancetype)initWithArchivePath:(NSString *)archivePath updateHostBundlePath:(NSString *)updateHostBundlePath; +- (instancetype)initWithArchivePath:(NSString *)archivePath extractionDirectory:(NSString *)extractionDirectory updateHostBundlePath:(NSString *)updateHostBundlePath; @end diff --git a/Sparkle/SUBinaryDeltaUnarchiver.m b/Sparkle/SUBinaryDeltaUnarchiver.m index e9db993a17..e6cfc99659 100644 --- a/Sparkle/SUBinaryDeltaUnarchiver.m +++ b/Sparkle/SUBinaryDeltaUnarchiver.m @@ -20,6 +20,7 @@ @interface SUBinaryDeltaUnarchiver () @property (nonatomic, copy, readonly) NSString *archivePath; @property (nonatomic, copy, readonly) NSString *updateHostBundlePath; +@property (nonatomic, copy, readonly) NSString *extractionDirectory; @end @@ -27,6 +28,7 @@ @implementation SUBinaryDeltaUnarchiver @synthesize archivePath = _archivePath; @synthesize updateHostBundlePath = _updateHostBundlePath; +@synthesize extractionDirectory = _extractionDirectory; + (BOOL)canUnarchivePath:(NSString *)path { @@ -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; } @@ -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){ diff --git a/Sparkle/SUDiskImageUnarchiver.h b/Sparkle/SUDiskImageUnarchiver.h index f0de0462ba..b60e898222 100644 --- a/Sparkle/SUDiskImageUnarchiver.h +++ b/Sparkle/SUDiskImageUnarchiver.h @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN @interface SUDiskImageUnarchiver : NSObject -- (instancetype)initWithArchivePath:(NSString *)archivePath decryptionPassword:(nullable NSString *)decryptionPassword; +- (instancetype)initWithArchivePath:(NSString *)archivePath extractionDirectory:(NSString *)extractionDirectory decryptionPassword:(nullable NSString *)decryptionPassword; @end diff --git a/Sparkle/SUDiskImageUnarchiver.m b/Sparkle/SUDiskImageUnarchiver.m index 96fd2f7d6a..48f85ae402 100644 --- a/Sparkle/SUDiskImageUnarchiver.m +++ b/Sparkle/SUDiskImageUnarchiver.m @@ -17,6 +17,7 @@ @interface SUDiskImageUnarchiver () @property (nonatomic, copy, readonly) NSString *archivePath; @property (nullable, nonatomic, copy, readonly) NSString *decryptionPassword; +@property (nonatomic, copy, readonly) NSString *extractionDirectory; @end @@ -24,6 +25,7 @@ @implementation SUDiskImageUnarchiver @synthesize archivePath = _archivePath; @synthesize decryptionPassword = _decryptionPassword; +@synthesize extractionDirectory = _extractionDirectory; + (BOOL)canUnarchivePath:(NSString *)path { @@ -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; } @@ -158,7 +161,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]; // We skip any files in the DMG which are not readable. if (![manager isReadableFileAtPath:fromPath]) { diff --git a/Sparkle/SUFlatPackageUnarchiver.h b/Sparkle/SUFlatPackageUnarchiver.h index ca8e41f95d..96ecc315e5 100644 --- a/Sparkle/SUFlatPackageUnarchiver.h +++ b/Sparkle/SUFlatPackageUnarchiver.h @@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN // An unarchiver for flat packages that doesn't really do any unarchiving @interface SUFlatPackageUnarchiver : NSObject -- (instancetype)initWithFlatPackagePath:(NSString *)flatPackagePath; +- (instancetype)initWithFlatPackagePath:(NSString *)flatPackagePath extractionDirectory:(NSString *)extractionDirectory; @end diff --git a/Sparkle/SUFlatPackageUnarchiver.m b/Sparkle/SUFlatPackageUnarchiver.m index 704ea295e3..39d8b24c40 100644 --- a/Sparkle/SUFlatPackageUnarchiver.m +++ b/Sparkle/SUFlatPackageUnarchiver.m @@ -16,12 +16,14 @@ @interface SUFlatPackageUnarchiver () @property (nonatomic, readonly) NSString *flatPackagePath; +@property (nonatomic, readonly, copy) NSString *extractionDirectory; @end @implementation SUFlatPackageUnarchiver @synthesize flatPackagePath = _flatPackagePath; +@synthesize extractionDirectory = _extractionDirectory; + (BOOL)canUnarchivePath:(NSString *)path { @@ -34,11 +36,12 @@ + (BOOL)mustValidateBeforeExtraction return NO; } -- (instancetype)initWithFlatPackagePath:(NSString *)flatPackagePath; +- (instancetype)initWithFlatPackagePath:(NSString *)flatPackagePath extractionDirectory:(NSString *)extractionDirectory { self = [super init]; if (self != nil) { _flatPackagePath = [flatPackagePath copy]; + _extractionDirectory = [extractionDirectory copy]; } return self; } @@ -54,8 +57,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:©Error]) { + 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]; + } } } diff --git a/Sparkle/SUInstaller.m b/Sparkle/SUInstaller.m index 67e904170c..25783ab9bc 100644 --- a/Sparkle/SUInstaller.m +++ b/Sparkle/SUInstaller.m @@ -20,15 +20,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:(nullable BOOL *)isGuidedPtr { NSParameterAssert(inUpdateFolder); @@ -47,6 +38,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]; @@ -76,10 +95,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. diff --git a/Sparkle/SUPipedUnarchiver.h b/Sparkle/SUPipedUnarchiver.h index 6e9c682f2e..904201a7c7 100644 --- a/Sparkle/SUPipedUnarchiver.h +++ b/Sparkle/SUPipedUnarchiver.h @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN @interface SUPipedUnarchiver : NSObject -- (instancetype)initWithArchivePath:(NSString *)archivePath; +- (instancetype)initWithArchivePath:(NSString *)archivePath extractionDirectory:(NSString *)extractionDirectory; @end diff --git a/Sparkle/SUPipedUnarchiver.m b/Sparkle/SUPipedUnarchiver.m index dbd5f9ba02..7b5aa5420b 100644 --- a/Sparkle/SUPipedUnarchiver.m +++ b/Sparkle/SUPipedUnarchiver.m @@ -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 *)commandAndArgumentsConformingToTypeOfPath:(NSString *)path { @@ -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; } @@ -94,7 +97,7 @@ - (void)extractArchivePipingDataToCommand:(NSString *)command arguments:(NSArray // *** GETS CALLED ON NON-MAIN THREAD!!! @autoreleasepool { NSError *error = nil; - NSString *destination = [self.archivePath stringByDeletingLastPathComponent]; + NSString *destination = self.extractionDirectory; SULog(SULogLevelDefault, @"Extracting using '%@' '%@' < '%@' '%@'", command, [args componentsJoinedByString:@"' '"], self.archivePath, destination); diff --git a/Sparkle/SUUnarchiver.h b/Sparkle/SUUnarchiver.h index a52bf5a2dd..b626ec3dbf 100644 --- a/Sparkle/SUUnarchiver.h +++ b/Sparkle/SUUnarchiver.h @@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN @interface SUUnarchiver : NSObject -+ (nullable id )unarchiverForPath:(NSString *)path updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword; ++ (nullable id )unarchiverForPath:(NSString *)path extractionDirectory:(NSString *)extractionDirectory updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword; @end diff --git a/Sparkle/SUUnarchiver.m b/Sparkle/SUUnarchiver.m index 8db211f969..5a47d2390b 100644 --- a/Sparkle/SUUnarchiver.m +++ b/Sparkle/SUUnarchiver.m @@ -18,21 +18,20 @@ @implementation SUUnarchiver -+ (nullable id )unarchiverForPath:(NSString *)path updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword ++ (nullable id )unarchiverForPath:(NSString *)path extractionDirectory:(NSString *)extractionDirectory updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword { 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]) { - return [[SUFlatPackageUnarchiver alloc] initWithFlatPackagePath:path]; + return [[SUFlatPackageUnarchiver alloc] initWithFlatPackagePath:path extractionDirectory:extractionDirectory]; } return nil; } diff --git a/Tests/SUUnarchiverTest.swift b/Tests/SUUnarchiverTest.swift index 13ada62563..81b8a69f55 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(appName).appendingPathExtension("app") - self.unarchiveTestAppWithExtension(archiveExtension, appName: appName, tempDirectoryURL: tempDirectoryURL, tempArchiveURL: tempArchiveURL, archiveResourceURL: archiveResourceURL, password: password, expectingSuccess: expectingSuccess, testExpectation: unarchivedSuccessExpectation) + self.unarchiveTestAppWithExtension(archiveExtension, appName: appName, tempDirectoryURL: tempDirectoryURL, archiveResourceURL: archiveResourceURL, password: password, expectingSuccess: expectingSuccess, testExpectation: unarchivedSuccessExpectation) self.unarchiveNonExistentFileTestFailureAppWithExtension(archiveExtension, tempDirectoryURL: tempDirectoryURL, password: password, testExpectation: unarchivedFailureExpectation) super.waitForExpectations(timeout: 30.0, handler: nil) @@ -40,7 +39,7 @@ class SUUnarchiverTest: XCTestCase func unarchiveNonExistentFileTestFailureAppWithExtension(_ archiveExtension: String, tempDirectoryURL: URL, password: String?, testExpectation: XCTestExpectation) { let tempArchiveURL = tempDirectoryURL.appendingPathComponent("error-invalid").appendingPathExtension(archiveExtension) - let unarchiver = SUUnarchiver.unarchiver(forPath: tempArchiveURL.path, updatingHostBundlePath: nil, decryptionPassword: password)! + let unarchiver = SUUnarchiver.unarchiver(forPath: tempArchiveURL.path, extractionDirectory: tempDirectoryURL.path, updatingHostBundlePath: nil, decryptionPassword: password)! unarchiver.unarchive(completionBlock: {(error: Error?) -> Void in XCTAssertNotNil(error) @@ -49,13 +48,8 @@ class SUUnarchiverTest: XCTestCase } // swiftlint:disable function_parameter_count - func unarchiveTestAppWithExtension(_ archiveExtension: String, appName: String, tempDirectoryURL: URL, tempArchiveURL: URL, archiveResourceURL: URL, password: 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)! + func unarchiveTestAppWithExtension(_ archiveExtension: String, appName: String, tempDirectoryURL: URL, archiveResourceURL: URL, password: String?, expectingSuccess: Bool, testExpectation: XCTestExpectation) { + let unarchiver = SUUnarchiver.unarchiver(forPath: archiveResourceURL.path, extractionDirectory: tempDirectoryURL.path, updatingHostBundlePath: nil, decryptionPassword: password)! unarchiver.unarchive(completionBlock: {(error: Error?) -> Void in if expectingSuccess { diff --git a/generate_appcast/Unarchive.swift b/generate_appcast/Unarchive.swift index 16bcc33a23..6303da3b98 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) { - 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) + if let unarchiver = SUUnarchiver.unarchiver(forPath: itemPath.path, extractionDirectory: archiveDestDir.path, updatingHostBundlePath: nil, decryptionPassword: nil) { + unarchiver.unarchive(completionBlock: { (error: Error?) in + if error != nil { + callback(error) + return + } + + callback(nil) + }, progressBlock: nil) + } else { + callback(makeError(code: .unarchivingError, "Not a supported archive format: \(itemPath)")) } }