Skip to content

Commit

Permalink
Extract archives in a separate directory from the input archive
Browse files Browse the repository at this point in the history
This is ported from the 2.x branch.
  • Loading branch information
zorgiepoo committed May 2, 2024
1 parent f453625 commit bbe887e
Show file tree
Hide file tree
Showing 14 changed files with 112 additions and 90 deletions.
39 changes: 26 additions & 13 deletions Sparkle/SUBasicUpdateDriver.m
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#import "SPUURLRequest.h"
#import "SPUDownloaderSession.h"
#import "SPULocalCacheDirectory.h"

@interface SUBasicUpdateDriver ()

Expand All @@ -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;
Expand All @@ -54,6 +56,7 @@ @implementation SUBasicUpdateDriver
@synthesize latestAppcastItemComparisonResult;
@synthesize download;
@synthesize downloadPath;
@synthesize extractionDirectory = _extractionDirectory;

@synthesize nonDeltaUpdateItem;
@synthesize tempDir;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -441,22 +445,30 @@ - (void)downloaderDidFailWithError:(NSError *)error

- (void)extractUpdate
{
id<SUUpdaterPrivate> updater = self.updater;
id<SUUnarchiverProtocol> 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<SUUpdaterPrivate> updater = self.updater;

id<SUUnarchiverProtocol> 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;
}
}
}

Expand Down Expand Up @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion Sparkle/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 Sparkle/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 Sparkle/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 Sparkle/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 @@ -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]) {
Expand Down
2 changes: 1 addition & 1 deletion Sparkle/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;
- (instancetype)initWithFlatPackagePath:(NSString *)flatPackagePath extractionDirectory:(NSString *)extractionDirectory;

@end

Expand Down
21 changes: 18 additions & 3 deletions Sparkle/SUFlatPackageUnarchiver.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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;
}
Expand All @@ -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:&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 Sparkle/SUInstaller.m
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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];
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion Sparkle/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 Sparkle/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 @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion Sparkle/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;
+ (nullable id <SUUnarchiverProtocol>)unarchiverForPath:(NSString *)path extractionDirectory:(NSString *)extractionDirectory updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword;

@end

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

@implementation SUUnarchiver

+ (nullable id <SUUnarchiverProtocol>)unarchiverForPath:(NSString *)path updatingHostBundlePath:(nullable NSString *)hostPath decryptionPassword:(nullable NSString *)decryptionPassword
+ (nullable id <SUUnarchiverProtocol>)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;
}
Expand Down
Loading

0 comments on commit bbe887e

Please sign in to comment.