Skip to content

Commit b6f7f60

Browse files
author
Julius Parishy
committed
Fix for Issue ZipArchive#16
Fixes issues with files being truncated when their headers have values that represent smaller uncompressed sizes then the actual data. This generally fixes issues where the whatever program zipped the file fucked up the headers. The fix makes ssziparchive behave like other unzip utilities that ignore the header and decompress the actual data until it's finished. I've tested a bunch and all of the tests included pass, if any other issues arrive I'd be glad to take additional time to look into it. Includes a file, IncorrectHeaders.zip, that is a zip of a folder called IncorrectHeaders which includes a single file, Readme.txt. I've intentionally changed the header for that file to read 50 bytes intend of the actual 59 for testing purposes.
1 parent f6eb58c commit b6f7f60

File tree

4 files changed

+49
-0
lines changed

4 files changed

+49
-0
lines changed

Tests/IncorrectHeaders.zip

967 Bytes
Binary file not shown.

Tests/SSZipArchive.xcodeproj/project.pbxproj

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
B215FB6B143AD576003AC546 /* zip.c in Sources */ = {isa = PBXBuildFile; fileRef = B215FB55143AD460003AC546 /* zip.c */; };
1919
B215FB6D143AD6FF003AC546 /* TestArchive.zip in Resources */ = {isa = PBXBuildFile; fileRef = B215FB6C143AD6FF003AC546 /* TestArchive.zip */; };
2020
B23FCC7F1558F1B70026375C /* TestPasswordArchive.zip in Resources */ = {isa = PBXBuildFile; fileRef = B23FCC7E1558F1B70026375C /* TestPasswordArchive.zip */; };
21+
C5AE4E64155A12760045F3ED /* IncorrectHeaders.zip in Resources */ = {isa = PBXBuildFile; fileRef = C5AE4E63155A12760045F3ED /* IncorrectHeaders.zip */; };
2122
/* End PBXBuildFile section */
2223

2324
/* Begin PBXFileReference section */
@@ -40,6 +41,7 @@
4041
B215FB64143AD527003AC546 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
4142
B215FB6C143AD6FF003AC546 /* TestArchive.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = TestArchive.zip; sourceTree = "<group>"; };
4243
B23FCC7E1558F1B70026375C /* TestPasswordArchive.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = TestPasswordArchive.zip; sourceTree = "<group>"; };
44+
C5AE4E63155A12760045F3ED /* IncorrectHeaders.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = IncorrectHeaders.zip; sourceTree = "<group>"; };
4345
/* End PBXFileReference section */
4446

4547
/* Begin PBXFrameworksBuildPhase section */
@@ -116,6 +118,7 @@
116118
children = (
117119
B215FB61143AD514003AC546 /* SSZipArchiveTests.m */,
118120
B215FB5F143AD514003AC546 /* SSZipArchiveTests-Info.plist */,
121+
C5AE4E63155A12760045F3ED /* IncorrectHeaders.zip */,
119122
B215FB6C143AD6FF003AC546 /* TestArchive.zip */,
120123
B23FCC7E1558F1B70026375C /* TestPasswordArchive.zip */,
121124
);
@@ -176,6 +179,7 @@
176179
files = (
177180
B215FB6D143AD6FF003AC546 /* TestArchive.zip in Resources */,
178181
B23FCC7F1558F1B70026375C /* TestPasswordArchive.zip in Resources */,
182+
C5AE4E64155A12760045F3ED /* IncorrectHeaders.zip in Resources */,
179183
);
180184
runOnlyForDeploymentPostprocessing = 0;
181185
};

Tests/SSZipArchiveTests.m

+31
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88

99
#import "SSZipArchive.h"
1010
#import <SenTestingKit/SenTestingKit.h>
11+
#import <CommonCrypto/CommonDigest.h>
1112

1213
@interface SSZipArchiveTests : SenTestCase <SSZipArchiveDelegate>
1314

1415
- (NSString *)_cachesPath:(NSString *)directory;
1516

17+
- (NSString *)_calculateMD5Digest:(NSData *)data;
18+
1619
@end
1720

1821
@implementation SSZipArchiveTests
@@ -66,6 +69,21 @@ - (void)testUnzippingWithPassword {
6669
STAssertTrue([fileManager fileExistsAtPath:testPath], @"LICENSE unzipped");
6770
}
6871

72+
- (void)testUnzippingTruncatedFileFix {
73+
NSString* zipPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"IncorrectHeaders" ofType:@"zip"];
74+
NSString* outputPath = [self _cachesPath:@"IncorrectHeaders"];
75+
76+
[SSZipArchive unzipFileAtPath:zipPath toDestination:outputPath delegate:self];
77+
78+
NSString* intendedReadmeTxtMD5 = @"31ac96301302eb388070c827447290b5";
79+
80+
NSString* filePath = [outputPath stringByAppendingPathComponent:@"IncorrectHeaders/Readme.txt"];
81+
NSData* data = [NSData dataWithContentsOfFile:filePath];
82+
83+
NSString* actualReadmeTxtMD5 = [self _calculateMD5Digest:data];
84+
STAssertTrue([actualReadmeTxtMD5 isEqualToString:intendedReadmeTxtMD5], @"Readme.txt MD5 digest should match original.");
85+
}
86+
6987

7088
// Commented out to avoid checking in several gig file into the repository. Simply add a file named
7189
// `LargeArchive.zip` to the project and uncomment out these lines to test.
@@ -117,4 +135,17 @@ - (NSString *)_cachesPath:(NSString *)directory {
117135
return path;
118136
}
119137

138+
- (NSString *)_calculateMD5Digest:(NSData *)data
139+
{
140+
unsigned char buffer[CC_MD5_DIGEST_LENGTH];
141+
CC_MD5([data bytes], [data length], buffer);
142+
143+
NSMutableString* digest = [NSMutableString string];
144+
145+
for(int i = 0; i < CC_MD5_DIGEST_LENGTH; ++i)
146+
[digest appendFormat:@"%02x", buffer[i]];
147+
148+
return digest;
149+
}
150+
120151
@end

minizip/unzip.c

+14
Original file line numberDiff line numberDiff line change
@@ -1718,10 +1718,24 @@ extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len)
17181718

17191719
pfile_in_zip_read_info->stream.avail_out = (uInt)len;
17201720

1721+
// NOTE:
1722+
// This bit of code seems to try to set the amount of space in the output buffer based on the
1723+
// value stored in the headers stored in the .zip file. However, if those values are incorrect
1724+
// it may result in a loss of data when uncompresssing that file. The compressed data is still
1725+
// legit and will deflate without knowing the uncompressed code so this tidbit is unnecessary and
1726+
// may cause issues for some .zip files.
1727+
//
1728+
// It's removed in here to fix those issues.
1729+
//
1730+
// See: https://github.com/samsoffes/ssziparchive/issues/16
1731+
//
1732+
1733+
/*
17211734
if ((len>pfile_in_zip_read_info->rest_read_uncompressed) &&
17221735
(!(pfile_in_zip_read_info->raw)))
17231736
pfile_in_zip_read_info->stream.avail_out =
17241737
(uInt)pfile_in_zip_read_info->rest_read_uncompressed;
1738+
*/
17251739

17261740
if ((len>pfile_in_zip_read_info->rest_read_compressed+
17271741
pfile_in_zip_read_info->stream.avail_in) &&

0 commit comments

Comments
 (0)