diff --git a/Autoupdate/SPUDeltaArchive.m b/Autoupdate/SPUDeltaArchive.m index e2f1e40b8..a779dd275 100644 --- a/Autoupdate/SPUDeltaArchive.m +++ b/Autoupdate/SPUDeltaArchive.m @@ -10,7 +10,7 @@ #import "SPUDeltaArchiveProtocol.h" #import "SPUSparkleDeltaArchive.h" #import "SPUXarDeltaArchive.h" -#import +#import "SUBinaryDeltaCommon.h" #include "AppKitPrevention.h" @@ -80,8 +80,8 @@ - (instancetype)initWithRelativeFilePath:(NSString *)relativeFilePath commands:( @implementation SPUDeltaArchiveHeader { - unsigned char _beforeTreeHash[CC_SHA1_DIGEST_LENGTH]; - unsigned char _afterTreeHash[CC_SHA1_DIGEST_LENGTH]; + unsigned char _beforeTreeHash[BINARY_DELTA_HASH_LENGTH]; + unsigned char _afterTreeHash[BINARY_DELTA_HASH_LENGTH]; } @synthesize compression = _compression; diff --git a/Autoupdate/SPUSparkleDeltaArchive.m b/Autoupdate/SPUSparkleDeltaArchive.m index f85019843..90da37490 100644 --- a/Autoupdate/SPUSparkleDeltaArchive.m +++ b/Autoupdate/SPUSparkleDeltaArchive.m @@ -338,18 +338,18 @@ - (nullable SPUDeltaArchiveHeader *)readHeader return nil; } - unsigned char beforeTreeHash[CC_SHA1_DIGEST_LENGTH] = {0}; + unsigned char beforeTreeHash[BINARY_DELTA_HASH_LENGTH] = {0}; if (![self _readBuffer:beforeTreeHash length:sizeof(beforeTreeHash)]) { return nil; } - unsigned char afterTreeHash[CC_SHA1_DIGEST_LENGTH] = {0}; + unsigned char afterTreeHash[BINARY_DELTA_HASH_LENGTH] = {0}; if (![self _readBuffer:afterTreeHash length:sizeof(afterTreeHash)]) { return nil; } NSDate *bundleCreationDate; - if (MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBinaryDeltaMajorVersion4)) { + if (majorVersion >= SUBinaryDeltaMajorVersion4) { double bundleCreationTimeInterval = 0; if (![self _readBuffer:&bundleCreationTimeInterval length:sizeof(bundleCreationTimeInterval)]) { return nil; @@ -865,10 +865,10 @@ - (void)writeHeader:(SPUDeltaArchiveHeader *)header uint16_t minorVersion = header.minorVersion; [self _writeBuffer:&minorVersion length:sizeof(minorVersion)]; - [self _writeBuffer:header.beforeTreeHash length:CC_SHA1_DIGEST_LENGTH]; - [self _writeBuffer:header.afterTreeHash length:CC_SHA1_DIGEST_LENGTH]; + [self _writeBuffer:header.beforeTreeHash length:BINARY_DELTA_HASH_LENGTH]; + [self _writeBuffer:header.afterTreeHash length:BINARY_DELTA_HASH_LENGTH]; - if (MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBinaryDeltaMajorVersion4)) { + if (majorVersion >= SUBinaryDeltaMajorVersion4) { NSDate *bundleCreationDate = header.bundleCreationDate; // If bundleCreationDate == nil, we will write out a 0 time interval diff --git a/Autoupdate/SPUXarDeltaArchive.m b/Autoupdate/SPUXarDeltaArchive.m index cb859c7ae..7e15d9c65 100644 --- a/Autoupdate/SPUXarDeltaArchive.m +++ b/Autoupdate/SPUXarDeltaArchive.m @@ -159,10 +159,10 @@ - (nullable SPUDeltaArchiveHeader *)readHeader } } - unsigned char rawExpectedBeforeHash[CC_SHA1_DIGEST_LENGTH] = {0}; + unsigned char rawExpectedBeforeHash[BINARY_DELTA_HASH_LENGTH] = {0}; getRawHashFromDisplayHash(rawExpectedBeforeHash, expectedBeforeHash); - unsigned char rawExpectedAfterHash[CC_SHA1_DIGEST_LENGTH] = {0}; + unsigned char rawExpectedAfterHash[BINARY_DELTA_HASH_LENGTH] = {0}; getRawHashFromDisplayHash(rawExpectedAfterHash, expectedAfterHash); // I wasn't able to figure out how to retrieve the compression options from xar, diff --git a/Autoupdate/SUBinaryDeltaApply.m b/Autoupdate/SUBinaryDeltaApply.m index f3a456131..6e3df0ce5 100644 --- a/Autoupdate/SUBinaryDeltaApply.m +++ b/Autoupdate/SUBinaryDeltaApply.m @@ -92,7 +92,7 @@ BOOL applyBinaryDelta(NSString *source, NSString *finalDestination, NSString *pa progressCallback(1/7.0); - unsigned char beforeHash[CC_SHA1_DIGEST_LENGTH] = {0}; + unsigned char beforeHash[BINARY_DELTA_HASH_LENGTH] = {0}; if (!getRawHashOfTreeWithVersion(beforeHash, source, majorDiffVersion)) { if (verbose) { fprintf(stderr, "\n"); @@ -102,8 +102,8 @@ BOOL applyBinaryDelta(NSString *source, NSString *finalDestination, NSString *pa } return NO; } - - if (memcmp(beforeHash, expectedBeforeHash, CC_SHA1_DIGEST_LENGTH) != 0) { + + if (memcmp(beforeHash, expectedBeforeHash, BINARY_DELTA_HASH_LENGTH) != 0) { if (verbose) { fprintf(stderr, "\n"); } @@ -460,7 +460,7 @@ BOOL applyBinaryDelta(NSString *source, NSString *finalDestination, NSString *pa fprintf(stderr, "\nVerifying destination..."); } - unsigned char afterHash[CC_SHA1_DIGEST_LENGTH] = {0}; + unsigned char afterHash[BINARY_DELTA_HASH_LENGTH] = {0}; if (!getRawHashOfTreeWithVersion(afterHash, finalDestination, majorDiffVersion)) { if (verbose) { fprintf(stderr, "\n"); @@ -471,8 +471,8 @@ BOOL applyBinaryDelta(NSString *source, NSString *finalDestination, NSString *pa removeTree(finalDestination); return NO; } - - if (memcmp(afterHash, expectedAfterHash, CC_SHA1_DIGEST_LENGTH) != 0) { + + if (memcmp(afterHash, expectedAfterHash, BINARY_DELTA_HASH_LENGTH) != 0) { if (verbose) { fprintf(stderr, "\n"); } diff --git a/Autoupdate/SUBinaryDeltaCommon.h b/Autoupdate/SUBinaryDeltaCommon.h index e6187ccc8..7b1bfc914 100644 --- a/Autoupdate/SUBinaryDeltaCommon.h +++ b/Autoupdate/SUBinaryDeltaCommon.h @@ -32,8 +32,6 @@ #define VERBOSE_MODIFIED "Modified" // file's metadata is modified #define VERBOSE_CLONED "Cloned" // file is cloned in content from a differently named file -#define MAJOR_VERSION_IS_AT_LEAST(actualMajor, expectedMajor) (actualMajor >= expectedMajor) - // Relative path of custom icon data that may be set on a bundle via a resource fork #define CUSTOM_ICON_PATH @"/Icon\r" @@ -58,12 +56,16 @@ extern SUBinaryDeltaMajorVersion SUBinaryDeltaMajorVersionFirstSupported; //#define COMPRESSION_LEVEL_ARGUMENT_DESCRIPTION @"The compression level to use for generating delta updates. This only applies if the compression method used is bzip2 which accepts values from 1 - 9. A special value of 0 will use the default compression level." +// This is the same as CC_SHA1_DIGEST_LENGTH +// Major versions >= 4 use a crc32 hash (using a subset of these bytes) while older versions use a sha1 hash +#define BINARY_DELTA_HASH_LENGTH 20 + SPUDeltaCompressionMode deltaCompressionModeFromDescription(NSString *description, BOOL *requestValid); NSString *deltaCompressionStringFromMode(SPUDeltaCompressionMode mode); extern int compareFiles(const FTSENT **a, const FTSENT **b); -BOOL getRawHashOfTreeWithVersion(unsigned char *hashBuffer, NSString *path, uint16_t majorVersion); -BOOL getRawHashOfTreeAndFileTablesWithVersion(unsigned char *hashBuffer, NSString *path, uint16_t majorVersion, NSMutableDictionary *> *hashToFileKeyDictionary, NSMutableDictionary *fileKeyToHashDictionary); +BOOL getRawHashOfTreeWithVersion(void *hashBuffer, NSString *path, uint16_t majorVersion); +BOOL getRawHashOfTreeAndFileTablesWithVersion(void *hashBuffer, NSString *path, uint16_t majorVersion, NSMutableDictionary *> *hashToFileKeyDictionary, NSMutableDictionary *fileKeyToHashDictionary); NSString *displayHashFromRawHash(const unsigned char *hash); void getRawHashFromDisplayHash(unsigned char *hash, NSString *hexHash); extern NSString *hashOfTreeWithVersion(NSString *path, uint16_t majorVersion); diff --git a/Autoupdate/SUBinaryDeltaCommon.m b/Autoupdate/SUBinaryDeltaCommon.m index 0461b2d0f..90e5d50da 100644 --- a/Autoupdate/SUBinaryDeltaCommon.m +++ b/Autoupdate/SUBinaryDeltaCommon.m @@ -8,6 +8,7 @@ #include "SUBinaryDeltaCommon.h" #include +#include // for crc32() #include #include #include @@ -158,7 +159,7 @@ uint16_t latestMinorVersionForMajorVersion(SUBinaryDeltaMajorVersion majorVersio return stringWithFileSystemRepresentation(templateResult); } -static void _hashOfBuffer(unsigned char *hash, const char *buffer, ssize_t bufferLength) +static void _sha1HashOfBuffer(unsigned char *hash, const char *buffer, ssize_t bufferLength) { assert(bufferLength >= 0 && bufferLength <= UINT32_MAX); CC_SHA1_CTX hashContext; @@ -167,7 +168,59 @@ static void _hashOfBuffer(unsigned char *hash, const char *buffer, ssize_t buffe CC_SHA1_Final(hash, &hashContext); } -static BOOL _hashOfFileContents(unsigned char *hash, FTSENT *ent, void *tempBuffer, size_t tempBufferSize) +static BOOL _crc32HashOfFileContents(uLong *outChecksum, FTSENT *ent, void *tempBuffer, size_t tempBufferSize) +{ + uLong checksum = *outChecksum; + + if (ent->fts_info == FTS_SL) { + char linkDestination[MAXPATHLEN + 1]; + ssize_t linkDestinationLength = readlink(ent->fts_path, linkDestination, MAXPATHLEN); + if (linkDestinationLength < 0) { + perror("readlink"); + return NO; + } + + checksum = crc32(checksum, (const void *)linkDestination, (unsigned int)linkDestinationLength); + } else if (ent->fts_info == FTS_F) { + ssize_t fileSize = ent->fts_statp->st_size; + + uint64_t encodedFileSize = (uint64_t)fileSize; + checksum = crc32(checksum, (const void *)&encodedFileSize, sizeof(encodedFileSize)); + + if (fileSize > 0) { + FILE *file = fopen(ent->fts_path, "rb"); + if (file == NULL) { + perror("fopen"); + return NO; + } + + size_t bytesLeft = (size_t)fileSize; + while (bytesLeft > 0) { + size_t bytesToConsume = (bytesLeft >= tempBufferSize) ? tempBufferSize : bytesLeft; + + if (fread(tempBuffer, bytesToConsume, 1, file) < 1) { + perror("fread"); + fclose(file); + return NO; + } + + checksum = crc32(checksum, tempBuffer, (uInt)bytesToConsume); + + bytesLeft -= bytesToConsume; + } + + fclose(file); + } + } else { + return NO; + } + + *outChecksum = checksum; + + return YES; +} + +static BOOL _sha1HashOfFileContents(unsigned char *hash, FTSENT *ent, void *tempBuffer, size_t tempBufferSize) { if (ent->fts_info == FTS_SL) { char linkDestination[MAXPATHLEN + 1]; @@ -177,11 +230,11 @@ static BOOL _hashOfFileContents(unsigned char *hash, FTSENT *ent, void *tempBuff return NO; } - _hashOfBuffer(hash, linkDestination, linkDestinationLength); + _sha1HashOfBuffer(hash, linkDestination, linkDestinationLength); } else if (ent->fts_info == FTS_F) { ssize_t fileSize = ent->fts_statp->st_size; if (fileSize <= 0) { - _hashOfBuffer(hash, NULL, 0); + _sha1HashOfBuffer(hash, NULL, 0); } else { FILE *file = fopen(ent->fts_path, "rb"); if (file == NULL) { @@ -218,12 +271,12 @@ static BOOL _hashOfFileContents(unsigned char *hash, FTSENT *ent, void *tempBuff return YES; } -BOOL getRawHashOfTreeWithVersion(unsigned char *hashBuffer, NSString *path, uint16_t majorVersion) +BOOL getRawHashOfTreeWithVersion(void *hashBuffer, NSString *path, uint16_t majorVersion) { return getRawHashOfTreeAndFileTablesWithVersion(hashBuffer, path, majorVersion, nil, nil); } -BOOL getRawHashOfTreeAndFileTablesWithVersion(unsigned char *hashBuffer, NSString *path, uint16_t __unused majorVersion, NSMutableDictionary *> *hashToFileKeyDictionary, NSMutableDictionary *fileKeyToHashDictionary) +BOOL getRawHashOfTreeAndFileTablesWithVersion(void *hashBuffer, NSString *path, uint16_t majorVersion, NSMutableDictionary *> *hashToFileKeyDictionary, NSMutableDictionary *fileKeyToHashDictionary) { char pathBuffer[PATH_MAX] = { 0 }; if (![path getFileSystemRepresentation:pathBuffer maxLength:sizeof(pathBuffer)]) { @@ -246,7 +299,12 @@ BOOL getRawHashOfTreeAndFileTablesWithVersion(unsigned char *hashBuffer, NSStrin } CC_SHA1_CTX hashContext; - CC_SHA1_Init(&hashContext); + const uLong initialCrc32Value = crc32(0L, Z_NULL, 0); + uLong crc32ChecksumValue = initialCrc32Value; + + if (majorVersion < SUBinaryDeltaMajorVersion4) { + CC_SHA1_Init(&hashContext); + } // Ensure the path uses filesystem-specific Unicode normalization #1017 NSString *normalizedPath = stringWithFileSystemRepresentation(pathBuffer); @@ -263,32 +321,67 @@ BOOL getRawHashOfTreeAndFileTablesWithVersion(unsigned char *hashBuffer, NSStrin continue; } - unsigned char fileHash[CC_SHA1_DIGEST_LENGTH]; - if (!_hashOfFileContents(fileHash, ent, tempBuffer, tempBufferSize)) { - fts_close(fts); - free(tempBuffer); - return NO; + NSData *fileHashKey; + if (majorVersion >= SUBinaryDeltaMajorVersion4) { + if (ent->fts_info == FTS_D) { + // No need to hash any further values for directories + // We hash relative file path and file type later + fileHashKey = nil; + } else { + uLong fileContentsChecksum = initialCrc32Value; + if (!_crc32HashOfFileContents(&fileContentsChecksum, ent, tempBuffer, tempBufferSize)) { + fts_close(fts); + free(tempBuffer); + return NO; + } + + uint64_t encodedFileContentsChecksum = fileContentsChecksum; + + crc32ChecksumValue = crc32(crc32ChecksumValue, (const void *)&encodedFileContentsChecksum, sizeof(encodedFileContentsChecksum)); + + if (ent->fts_info == FTS_F) { + fileHashKey = [NSData dataWithBytes:&encodedFileContentsChecksum length:sizeof(encodedFileContentsChecksum)]; + } else { + fileHashKey = nil; + } + } + } else { + unsigned char fileHash[CC_SHA1_DIGEST_LENGTH]; + if (!_sha1HashOfFileContents(fileHash, ent, tempBuffer, tempBufferSize)) { + fts_close(fts); + free(tempBuffer); + return NO; + } + CC_SHA1_Update(&hashContext, fileHash, sizeof(fileHash)); + + if (ent->fts_info == FTS_F) { + fileHashKey = [NSData dataWithBytes:fileHash length:sizeof(fileHash)]; + } else { + fileHashKey = nil; + } } - CC_SHA1_Update(&hashContext, fileHash, sizeof(fileHash)); // For file hash tables we only track regular files - if (ent->fts_info == FTS_F) { - NSData *hashKey = [NSData dataWithBytes:fileHash length:sizeof(fileHash)]; - + if (fileHashKey != nil) { if (hashToFileKeyDictionary != nil) { - if (hashToFileKeyDictionary[hashKey] == nil) { - hashToFileKeyDictionary[hashKey] = [NSMutableArray array]; + if (hashToFileKeyDictionary[fileHashKey] == nil) { + hashToFileKeyDictionary[fileHashKey] = [NSMutableArray array]; } - [hashToFileKeyDictionary[hashKey] addObject:relativePath]; + [hashToFileKeyDictionary[fileHashKey] addObject:relativePath]; } if (fileKeyToHashDictionary != nil) { - fileKeyToHashDictionary[relativePath] = hashKey; + fileKeyToHashDictionary[relativePath] = fileHashKey; } } const char *relativePathBytes = [relativePath fileSystemRepresentation]; - CC_SHA1_Update(&hashContext, relativePathBytes, (CC_LONG)strlen(relativePathBytes)); + + if (majorVersion >= SUBinaryDeltaMajorVersion4) { + crc32ChecksumValue = crc32(crc32ChecksumValue, (const void *)relativePathBytes, (uInt)strlen(relativePathBytes)); + } else { + CC_SHA1_Update(&hashContext, relativePathBytes, (CC_LONG)strlen(relativePathBytes)); + } uint16_t mode = ent->fts_statp->st_mode; uint16_t type = ent->fts_info; @@ -299,15 +392,26 @@ BOOL getRawHashOfTreeAndFileTablesWithVersion(unsigned char *hashBuffer, NSStrin // hardcoding a value helps avoid differences between filesystems. uint16_t hashedPermissions = (ent->fts_info == FTS_SL) ? VALID_SYMBOLIC_LINK_PERMISSIONS : permissions; - CC_SHA1_Update(&hashContext, &type, sizeof(type)); - CC_SHA1_Update(&hashContext, &hashedPermissions, sizeof(hashedPermissions)); + if (majorVersion >= SUBinaryDeltaMajorVersion4) { + crc32ChecksumValue = crc32(crc32ChecksumValue, (const void *)&type, sizeof(type)); + crc32ChecksumValue = crc32(crc32ChecksumValue, (const void *)&hashedPermissions, sizeof(hashedPermissions)); + } else { + CC_SHA1_Update(&hashContext, &type, sizeof(type)); + CC_SHA1_Update(&hashContext, &hashedPermissions, sizeof(hashedPermissions)); + } } free(tempBuffer); fts_close(fts); - CC_SHA1_Final(hashBuffer, &hashContext); + if (majorVersion >= SUBinaryDeltaMajorVersion4) { + uint64_t encodedCrc32ChecksumValue = crc32ChecksumValue; + memset(hashBuffer, 0, BINARY_DELTA_HASH_LENGTH); + memcpy(hashBuffer, &encodedCrc32ChecksumValue, sizeof(encodedCrc32ChecksumValue)); + } else { + CC_SHA1_Final(hashBuffer, &hashContext); + } return YES; } @@ -319,7 +423,7 @@ void getRawHashFromDisplayHash(unsigned char *hash, NSString *hexHash) return; } - for (size_t blockIndex = 0; blockIndex < CC_SHA1_DIGEST_LENGTH; blockIndex++) { + for (size_t blockIndex = 0; blockIndex < BINARY_DELTA_HASH_LENGTH; blockIndex++) { const char *currentBlock = hexString + blockIndex * 2; char convertedBlock[3] = {currentBlock[0], currentBlock[1], '\0'}; hash[blockIndex] = (unsigned char)strtol(convertedBlock, NULL, 16); @@ -328,8 +432,8 @@ void getRawHashFromDisplayHash(unsigned char *hash, NSString *hexHash) NSString *displayHashFromRawHash(const unsigned char *hash) { - char hexHash[CC_SHA1_DIGEST_LENGTH * 2 + 1] = {0}; - for (size_t i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) { + char hexHash[BINARY_DELTA_HASH_LENGTH * 2 + 1] = {0}; + for (size_t i = 0; i < BINARY_DELTA_HASH_LENGTH; i++) { snprintf(hexHash + i * 2, 3, "%02x", hash[i]); } return @(hexHash); @@ -337,7 +441,7 @@ void getRawHashFromDisplayHash(unsigned char *hash, NSString *hexHash) NSString *hashOfTreeWithVersion(NSString *path, uint16_t majorVersion) { - unsigned char hash[CC_SHA1_DIGEST_LENGTH]; + unsigned char hash[BINARY_DELTA_HASH_LENGTH] = {0}; if (!getRawHashOfTreeWithVersion(hash, path, majorVersion)) { return nil; } diff --git a/Autoupdate/SUBinaryDeltaCreate.m b/Autoupdate/SUBinaryDeltaCreate.m index a94896334..3fe183450 100644 --- a/Autoupdate/SUBinaryDeltaCreate.m +++ b/Autoupdate/SUBinaryDeltaCreate.m @@ -506,9 +506,9 @@ BOOL createBinaryDelta(NSString *source, NSString *destination, NSString *patchF fts_close(fts); // This dictionary will help us keep track of clones - NSMutableDictionary *> *beforeHashToFileKeyDictionary = MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBinaryDeltaMajorVersion3) ? [NSMutableDictionary dictionary] : nil; + NSMutableDictionary *> *beforeHashToFileKeyDictionary = (majorVersion >= SUBinaryDeltaMajorVersion3) ? [NSMutableDictionary dictionary] : nil; - unsigned char beforeHash[CC_SHA1_DIGEST_LENGTH] = {0}; + unsigned char beforeHash[BINARY_DELTA_HASH_LENGTH] = {0}; if (!getRawHashOfTreeAndFileTablesWithVersion(beforeHash, source, majorVersion, beforeHashToFileKeyDictionary, nil)) { if (verbose) { fprintf(stderr, "\n"); @@ -644,7 +644,7 @@ BOOL createBinaryDelta(NSString *source, NSString *destination, NSString *patchF // If we find any executable files that are using file system compression, that is sufficient // for recording that the applier should re-apply file system compression. // We check for executable files because they are likely candidates to be compressed. - if (!foundFilesystemCompression && MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBinaryDeltaMajorVersion3) && ent->fts_info == FTS_F && (ent->fts_statp->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0 && (ent->fts_statp->st_flags & UF_COMPRESSED) != 0) { + if (!foundFilesystemCompression && (majorVersion >= SUBinaryDeltaMajorVersion3) && ent->fts_info == FTS_F && (ent->fts_statp->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0 && (ent->fts_statp->st_flags & UF_COMPRESSED) != 0) { foundFilesystemCompression = true; if (verbose) { @@ -684,9 +684,9 @@ BOOL createBinaryDelta(NSString *source, NSString *destination, NSString *patchF fts_close(fts); // This dictionary will help us keep track of clones - NSMutableDictionary *afterFileKeyToHashDictionary = MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBinaryDeltaMajorVersion3) ? [NSMutableDictionary dictionary] : nil; + NSMutableDictionary *afterFileKeyToHashDictionary = (majorVersion >= SUBinaryDeltaMajorVersion3) ? [NSMutableDictionary dictionary] : nil; - unsigned char afterHash[CC_SHA1_DIGEST_LENGTH] = {0}; + unsigned char afterHash[BINARY_DELTA_HASH_LENGTH] = {0}; if (!getRawHashOfTreeAndFileTablesWithVersion(afterHash, destination, majorVersion, nil, afterFileKeyToHashDictionary)) { if (verbose) { fprintf(stderr, "\n"); @@ -725,7 +725,7 @@ BOOL createBinaryDelta(NSString *source, NSString *destination, NSString *patchF // Record creation date of root bundle item NSDate *bundleCreationDate; - if (MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBinaryDeltaMajorVersion4)) { + if (majorVersion >= SUBinaryDeltaMajorVersion4) { NSError *fileAttributesError = nil; NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:destination error:&fileAttributesError]; @@ -769,7 +769,7 @@ BOOL createBinaryDelta(NSString *source, NSString *destination, NSString *patchF // Using a couple of heuristics we track if files have been moved to other locations within the app bundle NSMutableDictionary *frameworkVersionsSubstitutes = [NSMutableDictionary dictionary]; NSMutableDictionary *fileSubstitutes = [NSMutableDictionary dictionary]; - if (MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBinaryDeltaMajorVersion3)) { + if (majorVersion >= SUBinaryDeltaMajorVersion3) { // Heuristic #1: track if an old framework version was removed and a new framework version was added // Keep track of these prefixes in a dictionary // Eg: /Contents/Frameworks/Foo.framework/Versions/B/ (new) -> /Contents/Frameworks/Foo.framework/Versions/A/ (old) @@ -887,7 +887,7 @@ BOOL createBinaryDelta(NSString *source, NSString *destination, NSString *patchF NSNumber *newPermissions = nil; BOOL clonePermissionsChanged = NO; BOOL clonedBinaryDiff = NO; - NSString *clonedRelativePath = MAJOR_VERSION_IS_AT_LEAST(majorVersion, SUBinaryDeltaMajorVersion3) ? cloneableRelativePath(afterFileKeyToHashDictionary, beforeHashToFileKeyDictionary, frameworkVersionsSubstitutes, fileSubstitutes, originalTreeState, newInfo, key, &newPermissions, &clonePermissionsChanged, &clonedBinaryDiff) : nil; + NSString *clonedRelativePath = (majorVersion >= SUBinaryDeltaMajorVersion3) ? cloneableRelativePath(afterFileKeyToHashDictionary, beforeHashToFileKeyDictionary, frameworkVersionsSubstitutes, fileSubstitutes, originalTreeState, newInfo, key, &newPermissions, &clonePermissionsChanged, &clonedBinaryDiff) : nil; if (clonedRelativePath != nil) { if (clonedBinaryDiff) { NSDictionary *cloneInfo = originalTreeState[clonedRelativePath]; diff --git a/Configurations/ConfigCommon.xcconfig b/Configurations/ConfigCommon.xcconfig index 7e4d688a5..620161cc0 100644 --- a/Configurations/ConfigCommon.xcconfig +++ b/Configurations/ConfigCommon.xcconfig @@ -104,7 +104,7 @@ SPARKLE_VERSION_PATCH = 0 // This should be in SemVer format or empty, ie. "-beta.1" // These variables must have a space after the '=' too SPARKLE_VERSION_SUFFIX = -beta.1 -CURRENT_PROJECT_VERSION = 2040 +CURRENT_PROJECT_VERSION = 2041 MARKETING_VERSION = $(SPARKLE_VERSION_MAJOR).$(SPARKLE_VERSION_MINOR).$(SPARKLE_VERSION_PATCH)$(SPARKLE_VERSION_SUFFIX) ALWAYS_SEARCH_USER_PATHS = NO diff --git a/Sparkle.xcodeproj/project.pbxproj b/Sparkle.xcodeproj/project.pbxproj index c217828c6..d2a7b5c65 100644 --- a/Sparkle.xcodeproj/project.pbxproj +++ b/Sparkle.xcodeproj/project.pbxproj @@ -205,6 +205,7 @@ 723C8A551E2D60DB00C14942 /* SUTouchBarButtonGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 723C8A531E2D60DB00C14942 /* SUTouchBarButtonGroup.m */; }; 723C8A561E2D60DB00C14942 /* SUTouchBarButtonGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 723C8A531E2D60DB00C14942 /* SUTouchBarButtonGroup.m */; }; 723EDC3F26885A8E000BCBA4 /* testappcast_channels.xml in Resources */ = {isa = PBXBuildFile; fileRef = 723EDC3E26885A8E000BCBA4 /* testappcast_channels.xml */; }; + 7240852E2CA11C1400ED2FCD /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7240852D2CA11C1400ED2FCD /* libz.tbd */; }; 72464F701E1F31E000FB341C /* SUOperatingSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 726F2CE41BC9C33D001971A4 /* SUOperatingSystem.m */; }; 72464F7C1E2097F600FB341C /* SUOperatingSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 726F2CE41BC9C33D001971A4 /* SUOperatingSystem.m */; }; 72464F7E1E21ED8C00FB341C /* SUHost.m in Sources */ = {isa = PBXBuildFile; fileRef = 61EF67550E25B58D00F754E0 /* SUHost.m */; }; @@ -1204,6 +1205,7 @@ 723C8A531E2D60DB00C14942 /* SUTouchBarButtonGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SUTouchBarButtonGroup.m; sourceTree = ""; }; 723DFF962B3DFF6E00628E6C /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 723EDC3E26885A8E000BCBA4 /* testappcast_channels.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = testappcast_channels.xml; sourceTree = ""; }; + 7240852D2CA11C1400ED2FCD /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 7246E0A11C83B685003B4E75 /* SPUStandardUpdaterController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPUStandardUpdaterController.h; sourceTree = ""; }; 7246E0A21C83B685003B4E75 /* SPUStandardUpdaterController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPUStandardUpdaterController.m; sourceTree = ""; }; 724BB36C1D31D0B7005D534A /* InstallerConnection.xpc */ = {isa = PBXFileReference; explicitFileType = "wrapper.xpc-service"; includeInIndex = 0; path = InstallerConnection.xpc; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1633,6 +1635,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 7240852E2CA11C1400ED2FCD /* libz.tbd in Frameworks */, EA1E281722B645AE004AA304 /* libbsdiff.a in Frameworks */, 7267E5C91D3D8C4300D1BF90 /* CoreServices.framework in Frameworks */, 7267E5CA1D3D8C4800D1BF90 /* Foundation.framework in Frameworks */, @@ -1741,6 +1744,7 @@ 0867D69AFE84028FC02AAC07 /* Frameworks */ = { isa = PBXGroup; children = ( + 7240852D2CA11C1400ED2FCD /* libz.tbd */, 654F352629B154AE00B10EEB /* ImageIO.framework */, 725B81F92781AEA40041746F /* libcompression.tbd */, 0867D6A5FE840307C02AAC07 /* AppKit.framework */, diff --git a/Tests/SUUnarchiverTest.swift b/Tests/SUUnarchiverTest.swift index 754af2030..2db7aa969 100644 --- a/Tests/SUUnarchiverTest.swift +++ b/Tests/SUUnarchiverTest.swift @@ -34,7 +34,8 @@ class SUUnarchiverTest: XCTestCase let extractedAppURL = tempDirectoryURL.appendingPathComponent(extractedAppName).appendingPathExtension("app") XCTAssertTrue(fileManager.fileExists(atPath: extractedAppURL.path)) - XCTAssertEqual("6a60ab31430cfca8fb499a884f4a29f73e59b472", hashOfTree(extractedAppURL.path)) + XCTAssertEqual("6a60ab31430cfca8fb499a884f4a29f73e59b472", hashOfTreeWithVersion(extractedAppURL.path, 3)) + XCTAssertEqual("52111bc200000000000000000000000000000000", hashOfTree(extractedAppURL.path)) } else if archiveExtension != "pkg" { let extractedPackageURL = tempDirectoryURL.appendingPathComponent(extractedAppName).appendingPathExtension("pkg") XCTAssertTrue(fileManager.fileExists(atPath: extractedPackageURL.path))