From 6cc4206ddeefe86c75173551f82abd4634a5720e Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Fri, 6 Oct 2017 17:42:41 -0400 Subject: [PATCH 01/17] Added stubbed integrity check methods --- Classes/URKArchive.h | 5 +++++ Classes/URKArchive.mm | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/Classes/URKArchive.h b/Classes/URKArchive.h index 2db0a6a3..4ad99438 100644 --- a/Classes/URKArchive.h +++ b/Classes/URKArchive.h @@ -451,6 +451,11 @@ extern NSString *URKErrorDomain; */ - (BOOL)validatePassword; +- (BOOL)checkDataIntegrity:(NSError **)error; + +- (BOOL)checkDataIntegrity:(URKFileInfo *)fileInfo error:(NSError **)error; + +- (BOOL)checkDataIntegrityOfFile:(NSString *)filePath error:(NSError **)error; @end NS_ASSUME_NONNULL_END diff --git a/Classes/URKArchive.mm b/Classes/URKArchive.mm index 465c4f69..e599360d 100644 --- a/Classes/URKArchive.mm +++ b/Classes/URKArchive.mm @@ -1030,6 +1030,20 @@ - (BOOL)validatePassword return passwordIsGood; } +- (BOOL)checkDataIntegrity:(NSError * __autoreleasing *)error +{ + return NO; +} + +- (BOOL)checkDataIntegrity:(URKFileInfo *)fileInfo error:(NSError * __autoreleasing *)error +{ + return NO; +} + +- (BOOL)checkDataIntegrityOfFile:(NSString *)filePath error:(NSError * __autoreleasing *)error +{ + return NO; +} #pragma mark - Callback Functions From 021052fb104e2ee610ecd03a466a2bfd4f5123ca Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Fri, 6 Oct 2017 22:30:17 -0400 Subject: [PATCH 02/17] Removed unnecessary overload --- Classes/URKArchive.h | 2 -- Classes/URKArchive.mm | 5 ----- 2 files changed, 7 deletions(-) diff --git a/Classes/URKArchive.h b/Classes/URKArchive.h index 4ad99438..f143a802 100644 --- a/Classes/URKArchive.h +++ b/Classes/URKArchive.h @@ -453,8 +453,6 @@ extern NSString *URKErrorDomain; - (BOOL)checkDataIntegrity:(NSError **)error; -- (BOOL)checkDataIntegrity:(URKFileInfo *)fileInfo error:(NSError **)error; - - (BOOL)checkDataIntegrityOfFile:(NSString *)filePath error:(NSError **)error; @end diff --git a/Classes/URKArchive.mm b/Classes/URKArchive.mm index e599360d..193d966d 100644 --- a/Classes/URKArchive.mm +++ b/Classes/URKArchive.mm @@ -1035,11 +1035,6 @@ - (BOOL)checkDataIntegrity:(NSError * __autoreleasing *)error return NO; } -- (BOOL)checkDataIntegrity:(URKFileInfo *)fileInfo error:(NSError * __autoreleasing *)error -{ - return NO; -} - - (BOOL)checkDataIntegrityOfFile:(NSString *)filePath error:(NSError * __autoreleasing *)error { return NO; From d4617379ecb7e7a2b85911979f94c1d3294f2c27 Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Fri, 6 Oct 2017 22:30:39 -0400 Subject: [PATCH 03/17] Added test cases for data integrity check --- Tests/CheckDataTests.m | 111 +++++++++++++++++++++++ Tests/Test Data/Good CRC Archive.rar | Bin 0 -> 4624 bytes Tests/Test Data/Modified CRC Archive.rar | Bin 0 -> 4624 bytes Tests/URKArchiveTestCase.m | 1 + UnrarKit.xcodeproj/project.pbxproj | 4 + 5 files changed, 116 insertions(+) create mode 100644 Tests/CheckDataTests.m create mode 100644 Tests/Test Data/Good CRC Archive.rar create mode 100644 Tests/Test Data/Modified CRC Archive.rar diff --git a/Tests/CheckDataTests.m b/Tests/CheckDataTests.m new file mode 100644 index 00000000..40a55baa --- /dev/null +++ b/Tests/CheckDataTests.m @@ -0,0 +1,111 @@ +// +// CheckDataTests.m +// UnrarKit +// +// Created by Dov Frankel on 10/6/17. +// + +#import "URKArchiveTestCase.h" + +@interface CheckDataTests : URKArchiveTestCase @end + +@implementation CheckDataTests + +- (void)testCheckDataIntegrity_Good { + NSArray *testArchives = @[@"Test Archive.rar", + @"Test Archive (Password).rar", + @"Test Archive (Header Password).rar"]; + + for (NSString *testArchiveName in testArchives) { + NSLog(@"Testing data integrity of archive %@", testArchiveName); + NSURL *testArchiveURL = self.testFileURLs[testArchiveName]; + NSString *password = ([testArchiveName rangeOfString:@"Password"].location != NSNotFound + ? @"password" + : nil); + URKArchive *archive = [[URKArchive alloc] initWithURL:testArchiveURL password:password error:nil]; + + NSError *dataCheckError = nil; + BOOL success = [archive checkDataIntegrity:&dataCheckError]; + + XCTAssertTrue(success, @"Data integrity check failed for %@", testArchiveName); + XCTAssertNil(dataCheckError, @"Error returned by checkDataIntegrity: %@", dataCheckError); + } +} + +- (void)testCheckDataIntegrity_NotAnArchive { + NSURL *testArchiveURL = self.testFileURLs[@"Test File B.jpg"]; + URKArchive *archive = [[URKArchive alloc] initWithURL:testArchiveURL error:nil]; + + NSError *dataCheckError = nil; + BOOL success = [archive checkDataIntegrity:&dataCheckError]; + + XCTAssertFalse(success, @"Data integrity check passed for non-archive"); + XCTAssertNotNil(dataCheckError, @"No error returned by checkDataIntegrity"); +} + + + +- (void)testCheckDataIntegrity_ModifiedCRC { + NSURL *testArchiveURL = self.testFileURLs[@"Modified CRC Archive.rar"]; + URKArchive *archive = [[URKArchive alloc] initWithURL:testArchiveURL error:nil]; + + NSError *dataCheckError = nil; + BOOL success = [archive checkDataIntegrity:&dataCheckError]; + + XCTAssertFalse(success, @"Data integrity check passed for archive with a modified CRC"); + XCTAssertNotNil(dataCheckError, @"No error returned by checkDataIntegrity"); +} + +- (void)testCheckDataIntegrityForFile_Good { + NSArray *testArchives = @[@"Test Archive.rar", + @"Test Archive (Password).rar", + @"Test Archive (Header Password).rar"]; + + for (NSString *testArchiveName in testArchives) { + NSLog(@"Testing data integrity of file in archive %@", testArchiveName); + NSURL *testArchiveURL = self.testFileURLs[testArchiveName]; + NSString *password = ([testArchiveName rangeOfString:@"Password"].location != NSNotFound + ? @"password" + : nil); + URKArchive *archive = [[URKArchive alloc] initWithURL:testArchiveURL password:password error:nil]; + + NSError *listFilenamesError = nil; + NSArray *filenames = [archive listFilenames:&listFilenamesError]; + + XCTAssertNotNil(filenames, @"No file info returned for %@", testArchiveName); + XCTAssertNil(listFilenamesError, @"Error returned for %@: %@", testArchiveName, listFilenamesError); + + NSString *firstFilename = filenames.firstObject; + NSError *dataCheckError = nil; + BOOL success = [archive checkDataIntegrityOfFile:firstFilename error:&dataCheckError]; + + XCTAssertTrue(success, @"Data integrity check failed for %@ in %@", firstFilename, testArchiveName); + XCTAssertNil(dataCheckError, @"Error returned by checkDataIntegrity: %@", dataCheckError); + } +} + +- (void)testCheckDataIntegrityForFile_NotAnArchive { + NSURL *testArchiveURL = self.testFileURLs[@"Test File B.jpg"]; + URKArchive *archive = [[URKArchive alloc] initWithURL:testArchiveURL error:nil]; + + NSString *filename = @"README.md"; + NSError *dataCheckError = nil; + BOOL success = [archive checkDataIntegrityOfFile:filename error:&dataCheckError]; + + XCTAssertFalse(success, @"Data integrity check passed for non-archive"); + XCTAssertNotNil(dataCheckError, @"Error not returned by checkDataIntegrity"); +} + +- (void)testCheckDataIntegrityForFile_ModifiedCRC { + NSURL *testArchiveURL = self.testFileURLs[@"Modified CRC Archive.rar"]; + URKArchive *archive = [[URKArchive alloc] initWithURL:testArchiveURL error:nil]; + + NSString *filename = @"README.md"; + NSError *dataCheckError = nil; + BOOL success = [archive checkDataIntegrityOfFile:filename error:&dataCheckError]; + + XCTAssertFalse(success, @"Data integrity check passed for archive with modified CRC"); + XCTAssertNotNil(dataCheckError, @"Error not returned by checkDataIntegrity"); +} + +@end diff --git a/Tests/Test Data/Good CRC Archive.rar b/Tests/Test Data/Good CRC Archive.rar new file mode 100644 index 0000000000000000000000000000000000000000..1c4a52e9e2cdf13e3d63b434baf6e2068e8b85f5 GIT binary patch literal 4624 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 Date: Fri, 6 Oct 2017 23:15:40 -0400 Subject: [PATCH 04/17] Implemented data check methods --- Classes/URKArchive.h | 5 +++ Classes/URKArchive.mm | 70 +++++++++++++++++++++++++---- Resources/en.lproj/UnrarKit.strings | 3 ++ Tests/CheckDataTests.m | 4 +- UnrarKit.xcodeproj/project.pbxproj | 10 ++++- 5 files changed, 80 insertions(+), 12 deletions(-) diff --git a/Classes/URKArchive.h b/Classes/URKArchive.h index f143a802..c3b55858 100644 --- a/Classes/URKArchive.h +++ b/Classes/URKArchive.h @@ -98,6 +98,11 @@ typedef NS_ENUM(NSInteger, URKErrorCode) { * User cancelled the operation */ URKErrorCodeUserCancelled = 102, + + /** + * CRC of extracted data doesn't match file header + */ + URKErrorCodeCorruptData = 103, }; typedef NSString *const URKProgressInfoKey; diff --git a/Classes/URKArchive.mm b/Classes/URKArchive.mm index 193d966d..127da151 100644 --- a/Classes/URKArchive.mm +++ b/Classes/URKArchive.mm @@ -9,6 +9,8 @@ #import "UnrarKitMacros.h" #import "NSString+UnrarKit.h" +#import "zlib.h" + RarHppIgnore #import "rar.hpp" #pragma clang diagnostic pop @@ -1032,12 +1034,59 @@ - (BOOL)validatePassword - (BOOL)checkDataIntegrity:(NSError * __autoreleasing *)error { - return NO; + return [self checkDataIntegrityOfFile:(NSString *_Nonnull)nil error:error]; } - (BOOL)checkDataIntegrityOfFile:(NSString *)filePath error:(NSError * __autoreleasing *)error { - return NO; + URKCreateActivity("Checking Data Integrity"); + + URKLogInfo("Checking integrity of %{public}@", filePath ? filePath : @"whole archive"); + + __block BOOL corruptDataFound = YES; + __block URKFileInfo *corruptFileInfo; + __block uLong foundCRC; + + if (error) { + *error = nil; + } + + NSError *performOnDataError = nil; + [self performOnDataInArchive:^(URKFileInfo *fileInfo, NSData *fileData, BOOL *stop) { + URKCreateActivity("Iterating through each file"); + corruptDataFound = NO; // Set inside here so invalid archives are marked as corrupt + if (filePath && ![fileInfo.filename isEqualToString:filePath]) return; + + NSUInteger expectedCRC = fileInfo.CRC; + uLong actualCRC = crc32((uLong)0, (const Bytef*)fileData.bytes, (uint)fileData.length); + URKLogDebug("Checking integrity of %{public}@. Expected CRC: %010lu vs. Actual: %010lu", + fileInfo.filename, expectedCRC, actualCRC); + if (expectedCRC != actualCRC) { + corruptDataFound = YES; + corruptFileInfo = fileInfo; + foundCRC = actualCRC; + } + + if (filePath) *stop = YES; + } + error:&performOnDataError]; + + if (performOnDataError) { + if (error) { + *error = performOnDataError; + } + + URKLogError("Error checking data integrity: %{public}@", performOnDataError); + } + + if (corruptDataFound) { + NSString *errorName = nil; + [self assignError:error code:URKErrorCodeCorruptData errorName:&errorName]; + URKLogError("Corrupt data found (filename: %{public}@, expected CRC: %010lu, actual CRC: %010lu, Error: %{public}@ (%d)", + corruptFileInfo.filename, corruptFileInfo.CRC, foundCRC, errorName, URKErrorCodeCorruptData); + } + + return !corruptDataFound; } @@ -1287,7 +1336,12 @@ - (NSString *)errorNameForErrorCode:(NSInteger)errorCode detail:(NSString * __au case URKErrorCodeUserCancelled: errorName = @"ERAR_USER_CANCELLED"; detail = NSLocalizedStringFromTableInBundle(@"User cancelled the operation in progress", @"UnrarKit", _resources, @"Error detail string"); - break; + break; + + case URKErrorCodeCorruptData: + errorName = @"ERAR_CORRUPT_DATA"; + detail = NSLocalizedStringFromTableInBundle(@"Data extracted from archive doesn't match what was written", @"UnrarKit", _resources, @"Error detail string"); + break; default: errorName = [NSString stringWithFormat:@"Unknown (%ld)", (long)errorCode]; @@ -1301,12 +1355,12 @@ - (NSString *)errorNameForErrorCode:(NSInteger)errorCode detail:(NSString * __au - (BOOL)assignError:(NSError * __autoreleasing *)error code:(NSInteger)errorCode errorName:(NSString * __autoreleasing *)outErrorName { - if (error) { - NSAssert(outErrorName, @"An out variable for errorName must be provided"); - - NSString *errorDetail = nil; - *outErrorName = [self errorNameForErrorCode:errorCode detail:&errorDetail]; + NSAssert(outErrorName, @"An out variable for errorName must be provided"); + + NSString *errorDetail = nil; + *outErrorName = [self errorNameForErrorCode:errorCode detail:&errorDetail]; + if (error) { NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary: @{NSLocalizedFailureReasonErrorKey: *outErrorName, NSLocalizedDescriptionKey: errorDetail, diff --git a/Resources/en.lproj/UnrarKit.strings b/Resources/en.lproj/UnrarKit.strings index 9e110bf3..9e77ca8d 100644 --- a/Resources/en.lproj/UnrarKit.strings +++ b/Resources/en.lproj/UnrarKit.strings @@ -7,6 +7,9 @@ /* Error detail string */ "Buffer too small to contain entire comments" = "Buffer too small to contain entire comments"; +/* Error detail string */ +"Data extracted from archive doesn't match what was written" = "Data extracted from archive doesn't match what was written"; + /* Error detail string */ "Error encountered while closing file" = "Error encountered while closing file"; diff --git a/Tests/CheckDataTests.m b/Tests/CheckDataTests.m index 40a55baa..d328382e 100644 --- a/Tests/CheckDataTests.m +++ b/Tests/CheckDataTests.m @@ -11,7 +11,7 @@ @interface CheckDataTests : URKArchiveTestCase @end @implementation CheckDataTests -- (void)testCheckDataIntegrity_Good { +- (void)testCheckDataIntegrity { NSArray *testArchives = @[@"Test Archive.rar", @"Test Archive (Password).rar", @"Test Archive (Header Password).rar"]; @@ -56,7 +56,7 @@ - (void)testCheckDataIntegrity_ModifiedCRC { XCTAssertNotNil(dataCheckError, @"No error returned by checkDataIntegrity"); } -- (void)testCheckDataIntegrityForFile_Good { +- (void)testCheckDataIntegrityForFile { NSArray *testArchives = @[@"Test Archive.rar", @"Test Archive (Password).rar", @"Test Archive (Header Password).rar"]; diff --git a/UnrarKit.xcodeproj/project.pbxproj b/UnrarKit.xcodeproj/project.pbxproj index 6725933b..de250af9 100644 --- a/UnrarKit.xcodeproj/project.pbxproj +++ b/UnrarKit.xcodeproj/project.pbxproj @@ -1316,7 +1316,10 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; - OTHER_LDFLAGS = "-ObjC"; + OTHER_LDFLAGS = ( + "-ObjC", + "-lz", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.abbey-code.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1364,7 +1367,10 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; - OTHER_LDFLAGS = "-ObjC"; + OTHER_LDFLAGS = ( + "-ObjC", + "-lz", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.abbey-code.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; From 5d5ee5631106f955c5e1fa0d09c46909cf83677f Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Fri, 6 Oct 2017 23:20:43 -0400 Subject: [PATCH 05/17] Added documentation for new methods --- Classes/URKArchive.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Classes/URKArchive.h b/Classes/URKArchive.h index c3b55858..c18c5fa4 100644 --- a/Classes/URKArchive.h +++ b/Classes/URKArchive.h @@ -456,8 +456,29 @@ extern NSString *URKErrorDomain; */ - (BOOL)validatePassword; + +/** + Extract each file in the archive, checking whether the data matches the CRC checksum + stored at the time it was written + + @param error Contains an NSError object when there was a problem reading the + archive, or when data is corrupt + + @return YES if the data is all correct, false if any check failed + */ - (BOOL)checkDataIntegrity:(NSError **)error; + +/** + Extract a particular file, to determine if its data matches the CRC + checksum stored at the time it written + + @param filePath The file in the archive to check + @param error Contains an NSError object when there was a problem reading the + archive, or when data is corrupt + + @return YES if the data is correct, false if any check failed + */ - (BOOL)checkDataIntegrityOfFile:(NSString *)filePath error:(NSError **)error; @end From f396d160a7f1429dab9922b21493325d7bad8d21 Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Fri, 6 Oct 2017 23:43:23 -0400 Subject: [PATCH 06/17] Refactored so checking a single file's data integrity doesn't extract every file in the archive --- Classes/URKArchive.mm | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/Classes/URKArchive.mm b/Classes/URKArchive.mm index 127da151..78c938d1 100644 --- a/Classes/URKArchive.mm +++ b/Classes/URKArchive.mm @@ -1046,18 +1046,29 @@ - (BOOL)checkDataIntegrityOfFile:(NSString *)filePath error:(NSError * __autorel __block BOOL corruptDataFound = YES; __block URKFileInfo *corruptFileInfo; __block uLong foundCRC; + __block NSError *innerError = nil; if (error) { *error = nil; } - NSError *performOnDataError = nil; - [self performOnDataInArchive:^(URKFileInfo *fileInfo, NSData *fileData, BOOL *stop) { + NSError *performOnFilesError = nil; + [self performOnFilesInArchive:^(URKFileInfo *fileInfo, BOOL *stop) { URKCreateActivity("Iterating through each file"); corruptDataFound = NO; // Set inside here so invalid archives are marked as corrupt if (filePath && ![fileInfo.filename isEqualToString:filePath]) return; - NSUInteger expectedCRC = fileInfo.CRC; + URKLogDebug("Extracting '%{public}@ to check its CRC...", fileInfo.filename); + NSError *extractError = nil; + NSData *fileData = [self extractData:fileInfo error:&extractError]; + if (!fileData) { + innerError = extractError; + URKLogError("Error extracting %{public}@: %{public}@", fileInfo.filename, extractError); + *stop = YES; + return; + } + + uLong expectedCRC = fileInfo.CRC; uLong actualCRC = crc32((uLong)0, (const Bytef*)fileData.bytes, (uint)fileData.length); URKLogDebug("Checking integrity of %{public}@. Expected CRC: %010lu vs. Actual: %010lu", fileInfo.filename, expectedCRC, actualCRC); @@ -1068,22 +1079,21 @@ - (BOOL)checkDataIntegrityOfFile:(NSString *)filePath error:(NSError * __autorel } if (filePath) *stop = YES; - } - error:&performOnDataError]; + } error:&performOnFilesError]; - if (performOnDataError) { + if (performOnFilesError) { if (error) { - *error = performOnDataError; + *error = performOnFilesError; } - URKLogError("Error checking data integrity: %{public}@", performOnDataError); + URKLogError("Error checking data integrity: %{public}@", performOnFilesError); } if (corruptDataFound) { NSString *errorName = nil; - [self assignError:error code:URKErrorCodeCorruptData errorName:&errorName]; + [self assignError:error code:URKErrorCodeCorruptData underlyer:innerError errorName:&errorName]; URKLogError("Corrupt data found (filename: %{public}@, expected CRC: %010lu, actual CRC: %010lu, Error: %{public}@ (%d)", - corruptFileInfo.filename, corruptFileInfo.CRC, foundCRC, errorName, URKErrorCodeCorruptData); + corruptFileInfo.filename, (uLong)corruptFileInfo.CRC, foundCRC, errorName, URKErrorCodeCorruptData); } return !corruptDataFound; @@ -1354,6 +1364,11 @@ - (NSString *)errorNameForErrorCode:(NSInteger)errorCode detail:(NSString * __au } - (BOOL)assignError:(NSError * __autoreleasing *)error code:(NSInteger)errorCode errorName:(NSString * __autoreleasing *)outErrorName +{ + return [self assignError:error code:errorCode underlyer:nil errorName:outErrorName]; +} + +- (BOOL)assignError:(NSError * __autoreleasing *)error code:(NSInteger)errorCode underlyer:(NSError *)underlyingError errorName:(NSString * __autoreleasing *)outErrorName { NSAssert(outErrorName, @"An out variable for errorName must be provided"); @@ -1370,6 +1385,10 @@ - (BOOL)assignError:(NSError * __autoreleasing *)error code:(NSInteger)errorCode userInfo[NSURLErrorKey] = self.fileURL; } + if (underlyingError) { + userInfo[NSUnderlyingErrorKey] = underlyingError; + } + *error = [NSError errorWithDomain:URKErrorDomain code:errorCode userInfo:userInfo]; From 013d79d63f345cbe889f1c67ec6e074a98e60dc4 Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Fri, 6 Oct 2017 23:57:44 -0400 Subject: [PATCH 07/17] Added link to zlib for CocoaPods --- UnrarKit.podspec | 1 + 1 file changed, 1 insertion(+) diff --git a/UnrarKit.podspec b/UnrarKit.podspec index 6a6c278c..2382be19 100644 --- a/UnrarKit.podspec +++ b/UnrarKit.podspec @@ -15,6 +15,7 @@ Pod::Spec.new do |s| s.resource_bundles = { 'UnrarKitResources' => ['Resources/**/*'] } + s.library = "z" s.subspec "unrar-lib" do |ss| ss.public_header_files = "Libraries/unrar/raros.hpp", From 70910544a8fc171c3a51e29c1b4b469f7e93a00e Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Sat, 7 Oct 2017 00:05:14 -0400 Subject: [PATCH 08/17] Updated changeling, reformatted new unit tests --- CHANGELOG.md | 1 + Tests/CheckDataTests.m | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8938635..a6a7f64a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Added support for `NSProgress` and `NSProgressReporting` in all extraction and iteration methods (Issue #34) * Added enhanced support for multivolume archives (PRs #59, #38 - Thanks to [@aonez](https://github.com/aonez) for the idea and implementation!) +* Added methods for checking data integrity of archived files (Issue #26, PR #61 - Thanks to [@amosavian](https://github.com/amosavian) for the suggestion!) * Added detailed logging using new unified logging framework. See [the readme](README.md) for more details (Issue #35) * Added localized details to returned `NSError` objects (Issue #45) * Moved `unrar` sources into a static library, and addressed a wide variety of warnings exposed by the `-Weverything` flag (Issue #56) diff --git a/Tests/CheckDataTests.m b/Tests/CheckDataTests.m index d328382e..2aa4bed7 100644 --- a/Tests/CheckDataTests.m +++ b/Tests/CheckDataTests.m @@ -11,6 +11,8 @@ @interface CheckDataTests : URKArchiveTestCase @end @implementation CheckDataTests +#pragma mark - checkDataIntegrity + - (void)testCheckDataIntegrity { NSArray *testArchives = @[@"Test Archive.rar", @"Test Archive (Password).rar", @@ -43,8 +45,6 @@ - (void)testCheckDataIntegrity_NotAnArchive { XCTAssertNotNil(dataCheckError, @"No error returned by checkDataIntegrity"); } - - - (void)testCheckDataIntegrity_ModifiedCRC { NSURL *testArchiveURL = self.testFileURLs[@"Modified CRC Archive.rar"]; URKArchive *archive = [[URKArchive alloc] initWithURL:testArchiveURL error:nil]; @@ -56,6 +56,8 @@ - (void)testCheckDataIntegrity_ModifiedCRC { XCTAssertNotNil(dataCheckError, @"No error returned by checkDataIntegrity"); } +#pragma mark - checkDataIntegrityOfFile + - (void)testCheckDataIntegrityForFile { NSArray *testArchives = @[@"Test Archive.rar", @"Test Archive (Password).rar", From ea8c1ebe68f4e9969668605cb16cbfcb688ee84d Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Sat, 7 Oct 2017 17:41:05 -0400 Subject: [PATCH 09/17] Updated test assertions --- Tests/ProgressReportingTests.m | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Tests/ProgressReportingTests.m b/Tests/ProgressReportingTests.m index 347f2687..a0ba66a1 100644 --- a/Tests/ProgressReportingTests.m +++ b/Tests/ProgressReportingTests.m @@ -64,7 +64,7 @@ - (void)testProgressReporting_ExtractFiles_FractionCompleted overwrite:NO error:&extractError]; - XCTAssertNil(extractError, @"Error returned by extractFilesTo:overwrite:progress:error:"); + XCTAssertNil(extractError, @"Error returned by extractFilesTo:overwrite:error:"); XCTAssertTrue(success, @"Unrar failed to extract %@ to %@", testArchiveName, extractURL); [extractFilesProgress resignCurrent]; @@ -113,7 +113,7 @@ - (void)testProgressReporting_ExtractFiles_Description overwrite:NO error:&extractError]; - XCTAssertNil(extractError, @"Error returned by extractFilesTo:overwrite:progress:error:"); + XCTAssertNil(extractError, @"Error returned by extractFilesTo:overwrite:error:"); XCTAssertTrue(success, @"Unrar failed to extract %@ to %@", testArchiveName, extractURL); [extractFilesProgress removeObserver:self forKeyPath:observedSelector]; @@ -153,7 +153,7 @@ - (void)testProgressReporting_ExtractFiles_AdditionalDescription overwrite:NO error:&extractError]; - XCTAssertNil(extractError, @"Error returned by extractFilesTo:overwrite:progress:error:"); + XCTAssertNil(extractError, @"Error returned by extractFilesTo:overwrite:error:"); XCTAssertTrue(success, @"Unrar failed to extract %@ to %@", testArchiveName, extractURL); [extractFilesProgress removeObserver:self forKeyPath:observedSelector]; @@ -194,7 +194,7 @@ - (void)testProgressReporting_ExtractFiles_FileInfo overwrite:NO error:&extractError]; - XCTAssertNil(extractError, @"Error returned by extractFilesTo:overwrite:progress:error:"); + XCTAssertNil(extractError, @"Error returned by extractFilesTo:overwrite:error:"); XCTAssertTrue(success, @"Unrar failed to extract %@ to %@", testArchiveName, extractURL); [extractFilesProgress removeObserver:self forKeyPath:observedSelector]; @@ -321,7 +321,7 @@ - (void)testProgressCancellation_ExtractFiles { overwrite:NO error:&extractError]; - XCTAssertNotNil(extractError, @"Error not returned by extractFilesTo:overwrite:progress:error:"); + XCTAssertNotNil(extractError, @"Error not returned by extractFilesTo:overwrite:error:"); XCTAssertEqual(extractError.code, URKErrorCodeUserCancelled, @"Incorrect error code returned from user cancellation"); XCTAssertFalse(success, @"Unrar didn't cancel extraction"); @@ -365,7 +365,7 @@ - (void)testProgressReporting_ExtractData { NSError *extractError = nil; NSData *data = [archive extractDataFromFile:firstFile error:&extractError]; - XCTAssertNil(extractError, @"Error returned by extractDataFromFile:progress:error:"); + XCTAssertNil(extractError, @"Error returned by extractDataFromFile:error:"); XCTAssertNotNil(data, @"Unrar failed to extract large archive"); [extractFileProgress resignCurrent]; @@ -409,7 +409,7 @@ - (void)testProgressReporting_ExtractBufferedData { error:&extractError action:^(NSData * _Nonnull dataChunk, CGFloat percentDecompressed) {}]; - XCTAssertNil(extractError, @"Error returned by extractDataFromFile:progress:error:"); + XCTAssertNil(extractError, @"Error returned by extractDataFromFile:error:"); XCTAssertTrue(success, @"Unrar failed to extract large archive into buffer"); [extractFileProgress resignCurrent]; @@ -451,7 +451,7 @@ - (void)testProgressCancellation_ExtractData { NSError *extractError = nil; NSData *data = [archive extractDataFromFile:firstFile error:&extractError]; - XCTAssertNotNil(extractError, @"No error returned by cancelled extractDataFromFile:progress:error:"); + XCTAssertNotNil(extractError, @"No error returned by cancelled extractDataFromFile:error:"); XCTAssertEqual(extractError.code, URKErrorCodeUserCancelled, @"Incorrect error code returned from user cancellation"); XCTAssertNil(data, @"extractData didn't return nil when cancelled"); @@ -487,7 +487,7 @@ - (void)testProgressCancellation_ExtractBufferedData { blockCallCount++; }]; - XCTAssertNotNil(extractError, @"No error returned by cancelled extractDataFromFile:progress:error:"); + XCTAssertNotNil(extractError, @"No error returned by cancelled extractDataFromFile:error:"); XCTAssertEqual(extractError.code, URKErrorCodeUserCancelled, @"Incorrect error code returned from user cancellation"); XCTAssertFalse(success, @"extractBufferedData didn't return false when cancelled"); From 3a986719589bda473e13f7c9a3e8493c2627cc88 Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Tue, 10 Oct 2017 07:58:45 -0400 Subject: [PATCH 10/17] Updated signatures of data integrity check methods to no longer return an NSError, since it's somewhat irrelevant. Any additional info needed can be gathered from the logs --- Classes/URKArchive.h | 9 ++------- Classes/URKArchive.mm | 31 ++++++------------------------- Tests/CheckDataTests.m | 31 ++++++------------------------- 3 files changed, 14 insertions(+), 57 deletions(-) diff --git a/Classes/URKArchive.h b/Classes/URKArchive.h index c18c5fa4..9be77c3a 100644 --- a/Classes/URKArchive.h +++ b/Classes/URKArchive.h @@ -461,12 +461,9 @@ extern NSString *URKErrorDomain; Extract each file in the archive, checking whether the data matches the CRC checksum stored at the time it was written - @param error Contains an NSError object when there was a problem reading the - archive, or when data is corrupt - @return YES if the data is all correct, false if any check failed */ -- (BOOL)checkDataIntegrity:(NSError **)error; +- (BOOL)checkDataIntegrity; /** @@ -474,12 +471,10 @@ extern NSString *URKErrorDomain; checksum stored at the time it written @param filePath The file in the archive to check - @param error Contains an NSError object when there was a problem reading the - archive, or when data is corrupt @return YES if the data is correct, false if any check failed */ -- (BOOL)checkDataIntegrityOfFile:(NSString *)filePath error:(NSError **)error; +- (BOOL)checkDataIntegrityOfFile:(NSString *)filePath; @end NS_ASSUME_NONNULL_END diff --git a/Classes/URKArchive.mm b/Classes/URKArchive.mm index 78c938d1..a502f3ce 100644 --- a/Classes/URKArchive.mm +++ b/Classes/URKArchive.mm @@ -1032,25 +1032,18 @@ - (BOOL)validatePassword return passwordIsGood; } -- (BOOL)checkDataIntegrity:(NSError * __autoreleasing *)error +- (BOOL)checkDataIntegrity { - return [self checkDataIntegrityOfFile:(NSString *_Nonnull)nil error:error]; + return [self checkDataIntegrityOfFile:(NSString *_Nonnull)nil]; } -- (BOOL)checkDataIntegrityOfFile:(NSString *)filePath error:(NSError * __autoreleasing *)error +- (BOOL)checkDataIntegrityOfFile:(NSString *)filePath { URKCreateActivity("Checking Data Integrity"); URKLogInfo("Checking integrity of %{public}@", filePath ? filePath : @"whole archive"); - + __block BOOL corruptDataFound = YES; - __block URKFileInfo *corruptFileInfo; - __block uLong foundCRC; - __block NSError *innerError = nil; - - if (error) { - *error = nil; - } NSError *performOnFilesError = nil; [self performOnFilesInArchive:^(URKFileInfo *fileInfo, BOOL *stop) { @@ -1062,7 +1055,6 @@ - (BOOL)checkDataIntegrityOfFile:(NSString *)filePath error:(NSError * __autorel NSError *extractError = nil; NSData *fileData = [self extractData:fileInfo error:&extractError]; if (!fileData) { - innerError = extractError; URKLogError("Error extracting %{public}@: %{public}@", fileInfo.filename, extractError); *stop = YES; return; @@ -1074,28 +1066,17 @@ - (BOOL)checkDataIntegrityOfFile:(NSString *)filePath error:(NSError * __autorel fileInfo.filename, expectedCRC, actualCRC); if (expectedCRC != actualCRC) { corruptDataFound = YES; - corruptFileInfo = fileInfo; - foundCRC = actualCRC; + URKLogError("Corrupt data found (filename: %{public}@, expected CRC: %010lu, actual CRC: %010lu", + fileInfo.filename, expectedCRC, actualCRC); } if (filePath) *stop = YES; } error:&performOnFilesError]; if (performOnFilesError) { - if (error) { - *error = performOnFilesError; - } - URKLogError("Error checking data integrity: %{public}@", performOnFilesError); } - if (corruptDataFound) { - NSString *errorName = nil; - [self assignError:error code:URKErrorCodeCorruptData underlyer:innerError errorName:&errorName]; - URKLogError("Corrupt data found (filename: %{public}@, expected CRC: %010lu, actual CRC: %010lu, Error: %{public}@ (%d)", - corruptFileInfo.filename, (uLong)corruptFileInfo.CRC, foundCRC, errorName, URKErrorCodeCorruptData); - } - return !corruptDataFound; } diff --git a/Tests/CheckDataTests.m b/Tests/CheckDataTests.m index 2aa4bed7..6228c67b 100644 --- a/Tests/CheckDataTests.m +++ b/Tests/CheckDataTests.m @@ -26,11 +26,8 @@ - (void)testCheckDataIntegrity { : nil); URKArchive *archive = [[URKArchive alloc] initWithURL:testArchiveURL password:password error:nil]; - NSError *dataCheckError = nil; - BOOL success = [archive checkDataIntegrity:&dataCheckError]; - + BOOL success = [archive checkDataIntegrity]; XCTAssertTrue(success, @"Data integrity check failed for %@", testArchiveName); - XCTAssertNil(dataCheckError, @"Error returned by checkDataIntegrity: %@", dataCheckError); } } @@ -38,22 +35,16 @@ - (void)testCheckDataIntegrity_NotAnArchive { NSURL *testArchiveURL = self.testFileURLs[@"Test File B.jpg"]; URKArchive *archive = [[URKArchive alloc] initWithURL:testArchiveURL error:nil]; - NSError *dataCheckError = nil; - BOOL success = [archive checkDataIntegrity:&dataCheckError]; - + BOOL success = [archive checkDataIntegrity]; XCTAssertFalse(success, @"Data integrity check passed for non-archive"); - XCTAssertNotNil(dataCheckError, @"No error returned by checkDataIntegrity"); } - (void)testCheckDataIntegrity_ModifiedCRC { NSURL *testArchiveURL = self.testFileURLs[@"Modified CRC Archive.rar"]; URKArchive *archive = [[URKArchive alloc] initWithURL:testArchiveURL error:nil]; - NSError *dataCheckError = nil; - BOOL success = [archive checkDataIntegrity:&dataCheckError]; - + BOOL success = [archive checkDataIntegrity]; XCTAssertFalse(success, @"Data integrity check passed for archive with a modified CRC"); - XCTAssertNotNil(dataCheckError, @"No error returned by checkDataIntegrity"); } #pragma mark - checkDataIntegrityOfFile @@ -78,11 +69,9 @@ - (void)testCheckDataIntegrityForFile { XCTAssertNil(listFilenamesError, @"Error returned for %@: %@", testArchiveName, listFilenamesError); NSString *firstFilename = filenames.firstObject; - NSError *dataCheckError = nil; - BOOL success = [archive checkDataIntegrityOfFile:firstFilename error:&dataCheckError]; + BOOL success = [archive checkDataIntegrityOfFile:firstFilename]; XCTAssertTrue(success, @"Data integrity check failed for %@ in %@", firstFilename, testArchiveName); - XCTAssertNil(dataCheckError, @"Error returned by checkDataIntegrity: %@", dataCheckError); } } @@ -90,24 +79,16 @@ - (void)testCheckDataIntegrityForFile_NotAnArchive { NSURL *testArchiveURL = self.testFileURLs[@"Test File B.jpg"]; URKArchive *archive = [[URKArchive alloc] initWithURL:testArchiveURL error:nil]; - NSString *filename = @"README.md"; - NSError *dataCheckError = nil; - BOOL success = [archive checkDataIntegrityOfFile:filename error:&dataCheckError]; - + BOOL success = [archive checkDataIntegrityOfFile:@"README.md"]; XCTAssertFalse(success, @"Data integrity check passed for non-archive"); - XCTAssertNotNil(dataCheckError, @"Error not returned by checkDataIntegrity"); } - (void)testCheckDataIntegrityForFile_ModifiedCRC { NSURL *testArchiveURL = self.testFileURLs[@"Modified CRC Archive.rar"]; URKArchive *archive = [[URKArchive alloc] initWithURL:testArchiveURL error:nil]; - NSString *filename = @"README.md"; - NSError *dataCheckError = nil; - BOOL success = [archive checkDataIntegrityOfFile:filename error:&dataCheckError]; - + BOOL success = [archive checkDataIntegrityOfFile:@"README.md"]; XCTAssertFalse(success, @"Data integrity check passed for archive with modified CRC"); - XCTAssertNotNil(dataCheckError, @"Error not returned by checkDataIntegrity"); } @end From 27a6b2c72f5cc99d30d117aee6f212faddfc3f52 Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Tue, 10 Oct 2017 13:39:43 -0400 Subject: [PATCH 11/17] Removed unused new error code --- Classes/URKArchive.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Classes/URKArchive.h b/Classes/URKArchive.h index 9be77c3a..784c0913 100644 --- a/Classes/URKArchive.h +++ b/Classes/URKArchive.h @@ -98,11 +98,6 @@ typedef NS_ENUM(NSInteger, URKErrorCode) { * User cancelled the operation */ URKErrorCodeUserCancelled = 102, - - /** - * CRC of extracted data doesn't match file header - */ - URKErrorCodeCorruptData = 103, }; typedef NSString *const URKProgressInfoKey; From 3e4c4bcfe41ca07f40b73497a6020de9c09b4dd6 Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Tue, 10 Oct 2017 13:40:59 -0400 Subject: [PATCH 12/17] Finished removing URKErrorCodeCorruptData --- Classes/URKArchive.mm | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Classes/URKArchive.mm b/Classes/URKArchive.mm index a502f3ce..16496847 100644 --- a/Classes/URKArchive.mm +++ b/Classes/URKArchive.mm @@ -1328,11 +1328,6 @@ - (NSString *)errorNameForErrorCode:(NSInteger)errorCode detail:(NSString * __au errorName = @"ERAR_USER_CANCELLED"; detail = NSLocalizedStringFromTableInBundle(@"User cancelled the operation in progress", @"UnrarKit", _resources, @"Error detail string"); break; - - case URKErrorCodeCorruptData: - errorName = @"ERAR_CORRUPT_DATA"; - detail = NSLocalizedStringFromTableInBundle(@"Data extracted from archive doesn't match what was written", @"UnrarKit", _resources, @"Error detail string"); - break; default: errorName = [NSString stringWithFormat:@"Unknown (%ld)", (long)errorCode]; From 2499a350ecb773a0877d43bfee064329ee133b1b Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Tue, 10 Oct 2017 13:41:51 -0400 Subject: [PATCH 13/17] Removed unused localized string --- Resources/en.lproj/UnrarKit.strings | 3 --- 1 file changed, 3 deletions(-) diff --git a/Resources/en.lproj/UnrarKit.strings b/Resources/en.lproj/UnrarKit.strings index 9e77ca8d..9e110bf3 100644 --- a/Resources/en.lproj/UnrarKit.strings +++ b/Resources/en.lproj/UnrarKit.strings @@ -7,9 +7,6 @@ /* Error detail string */ "Buffer too small to contain entire comments" = "Buffer too small to contain entire comments"; -/* Error detail string */ -"Data extracted from archive doesn't match what was written" = "Data extracted from archive doesn't match what was written"; - /* Error detail string */ "Error encountered while closing file" = "Error encountered while closing file"; From fbfe07f52f4b73d6d338cfe7c34309733bf5d52d Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Tue, 10 Oct 2017 16:35:27 -0400 Subject: [PATCH 14/17] Moved progress-based cancellation to the beginning of an iteration --- Classes/URKArchive.mm | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Classes/URKArchive.mm b/Classes/URKArchive.mm index a502f3ce..3ea191b6 100644 --- a/Classes/URKArchive.mm +++ b/Classes/URKArchive.mm @@ -723,6 +723,11 @@ - (BOOL)performOnFilesInArchive:(void(^)(URKFileInfo *fileInfo, BOOL *stop))acti URKCreateActivity("Iterating Each File Info"); [sortedFileInfo enumerateObjectsUsingBlock:^(URKFileInfo *info, NSUInteger idx, BOOL *stop) { + if (progress.isCancelled) { + URKLogInfo("PerformOnFiles iteration was cancelled"); + *stop = YES; + } + URKLogDebug("Performing action on %{public}@", info.filename); action(info, stop); progress.completedUnitCount += 1; @@ -731,11 +736,6 @@ - (BOOL)performOnFilesInArchive:(void(^)(URKFileInfo *fileInfo, BOOL *stop))acti URKLogInfo("Action dictated an early stop"); progress.completedUnitCount = progress.totalUnitCount; } - - if (progress.isCancelled) { - URKLogInfo("File info iteration was cancelled"); - *stop = YES; - } }]; } From bcf4f8067b5eecfff0b756bf48246d52bf35c93c Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Tue, 10 Oct 2017 16:43:13 -0400 Subject: [PATCH 15/17] Fixed bug in progress reporting --- Classes/URKArchive.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Classes/URKArchive.mm b/Classes/URKArchive.mm index 47889536..5a0f870e 100644 --- a/Classes/URKArchive.mm +++ b/Classes/URKArchive.mm @@ -1395,6 +1395,8 @@ - (NSProgress *)beginProgressOperation:(unsigned long long)totalUnitCount NSProgress *progress; progress = self.progress; + self.progress = nil; + if (!progress) { progress = [[NSProgress alloc] initWithParent:[NSProgress currentProgress] userInfo:nil]; From e6f68b12a4abf9438e0ad209425adb476d67fbd0 Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Tue, 10 Oct 2017 16:48:20 -0400 Subject: [PATCH 16/17] Fixed inconsistency in deprecation messages --- Classes/URKArchive.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Classes/URKArchive.h b/Classes/URKArchive.h index 784c0913..26947456 100644 --- a/Classes/URKArchive.h +++ b/Classes/URKArchive.h @@ -326,7 +326,7 @@ extern NSString *URKErrorDomain; - (BOOL)extractFilesTo:(NSString *)filePath overwrite:(BOOL)overwrite progress:(nullable void (^)(URKFileInfo *currentFile, CGFloat percentArchiveDecompressed))progressBlock - error:(NSError **)error __deprecated_msg("Use extractFilesTo:overwrite:error: instead, and if using the progress block, replace with NSProgress as described in the README"); + error:(NSError **)error __deprecated_msg("Use -extractFilesTo:overwrite:error: instead, and if using the progress block, replace with NSProgress as described in the README"); /** * Unarchive a single file from the archive into memory. Supports NSProgress for progress reporting, which also @@ -354,7 +354,7 @@ extern NSString *URKErrorDomain; */ - (nullable NSData *)extractData:(URKFileInfo *)fileInfo progress:(nullable void (^)(CGFloat percentDecompressed))progressBlock - error:(NSError **)error __deprecated_msg("Use extractData:error: instead, and if using the progress block, replace with NSProgress as described in the README"); + error:(NSError **)error __deprecated_msg("Use -extractData:error: instead, and if using the progress block, replace with NSProgress as described in the README"); /** * Unarchive a single file from the archive into memory. Supports NSProgress for progress reporting, which also @@ -385,7 +385,7 @@ extern NSString *URKErrorDomain; */ - (nullable NSData *)extractDataFromFile:(NSString *)filePath progress:(nullable void (^)(CGFloat percentDecompressed))progressBlock - error:(NSError **)error __deprecated_msg("Use extractDataFromFile:error: instead, and if using the progress block, replace with NSProgress as described in the README"); + error:(NSError **)error __deprecated_msg("Use -extractDataFromFile:error: instead, and if using the progress block, replace with NSProgress as described in the README"); /** * Loops through each file in the archive in alphabetical order, allowing you to perform an From bef2e4964430d72529d00ba341a0cc51f88d24cd Mon Sep 17 00:00:00 2001 From: Dov Frankel Date: Tue, 10 Oct 2017 17:36:00 -0400 Subject: [PATCH 17/17] Removed references to `progress` blocks from README --- README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.md b/README.md index b5b5e874..c7bca0a2 100644 --- a/README.md +++ b/README.md @@ -61,19 +61,12 @@ if (archive.isPasswordProtected) { ```Objective-C BOOL extractFilesSuccessful = [archive extractFilesTo:@"some/directory" overWrite:NO - progress: - ^(URKFileInfo *currentFile, CGFloat percentArchiveDecompressed) { - NSLog(@"Extracting %@: %f%% complete", currentFile.filename, percentArchiveDecompressed); - } error:&error]; ``` ## Extracting a file into memory ```Objective-C NSData *extractedData = [archive extractDataFromFile:@"a file in the archive.jpg" - progress:^(CGFloat percentDecompressed) { - NSLog(@"Extracting, %f%% complete", percentDecompressed); - } error:&error]; ```