diff --git a/zipzap/ZZArchive.h b/zipzap/ZZArchive.h index 299d6146..2c1805e2 100644 --- a/zipzap/ZZArchive.h +++ b/zipzap/ZZArchive.h @@ -50,7 +50,7 @@ /** * The array of entries within this archive. */ -@property (readonly, copy, nonatomic) NSArray* entries; +@property (readonly, nonatomic) NSArray* entries; /** * Creates a new archive with the zip file at the given file URL. @@ -111,10 +111,15 @@ @interface ZZMutableArchive : ZZArchive /** - * The array of entries within this archive. - * To write new entries in the zip file, set this property to a different array of entries. - * When you set this property, any old entries should be considered invalid. + * Updates the entries and writes them to the source. + * + * @param newEntries The entries to update to, may contain some or all existing entries. + * @param error The error information when an error occurs. Pass in *nil* if you do not want error information. + * @return Whether the update was successful or not. + * + * @remarks If the write fails and the entries are completely new, the existing zip file will be untouched. Instead, if the write fails and the entries contain some or all existing entries, the zip file may be corrupted. In this case, the error information will report the ZZReplaceWriteErrorCode error code. */ -@property (copy, nonatomic) NSArray* entries; +- (BOOL)updateEntries:(NSArray*)newEntries + error:(NSError**)error; @end \ No newline at end of file diff --git a/zipzap/ZZArchive.mm b/zipzap/ZZArchive.mm index 9daf8197..5cc52f26 100644 --- a/zipzap/ZZArchive.mm +++ b/zipzap/ZZArchive.mm @@ -94,7 +94,7 @@ - (BOOL)load:(NSError**)error NSError* __autoreleasing readError; NSData* contents = [_channel openInput:&readError]; if (!contents) - return ZZRaiseError(error, ZZReadErrorCode, @{NSUnderlyingErrorKey : readError}); + return ZZRaiseError(error, ZZOpenReadErrorCode, @{NSUnderlyingErrorKey : readError}); // search for the end of directory signature in last 64K of file const uint8_t* beginContent = (const uint8_t*)contents.bytes; @@ -125,7 +125,7 @@ - (BOOL)load:(NSError**)error // end of central directory occurs at actual end of the zip || endContent != endOfCentralDirectory + sizeof(ZZEndOfCentralDirectory) + endOfCentralDirectoryRecord->zipFileCommentLength) - return ZZRaiseError(error, ZZBadEndOfCentralDirectoryErrorCode, nil); + return ZZRaiseError(error, ZZEndOfCentralDirectoryReadErrorCode, nil); // add an entry for each central header in the sequence ZZCentralFileHeader* nextCentralFileHeader = (ZZCentralFileHeader*)(beginContent @@ -142,7 +142,7 @@ - (BOOL)load:(NSError**)error // local file occurs before first central file header, and has enough minimal space for at least local file || nextCentralFileHeader->relativeOffsetOfLocalHeader + sizeof(ZZLocalFileHeader) > endOfCentralDirectoryRecord->offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber) - return ZZRaiseError(error, ZZBadCentralFileErrorCode, @{ZZEntryIndexKey : @(index)}); + return ZZRaiseError(error, ZZCentralFileHeaderReadErrorCode, @{ZZEntryIndexKey : @(index)}); ZZLocalFileHeader* nextLocalFileHeader = (ZZLocalFileHeader*)(beginContent + nextCentralFileHeader->relativeOffsetOfLocalHeader); @@ -164,7 +164,8 @@ - (BOOL)load:(NSError**)error @implementation ZZMutableArchive -- (void)setEntries:(NSArray*)newEntries +- (BOOL)updateEntries:(NSArray*)newEntries + error:(NSError**)error { // NOTE: we want to avoid loading at all when entries are being overwritten, even in the face of lazy loading: // consider that nil _contents implies that no valid entries have been loaded, and newEntries cannot possibly contain any of our old entries @@ -182,10 +183,9 @@ - (void)setEntries:(NSArray*)newEntries // get an entry writer for each new entry NSMutableArray* newEntryWriters = [NSMutableArray array]; - [newEntries enumerateObjectsUsingBlock:^(ZZArchiveEntry *anEntry, NSUInteger idx, BOOL *stop) + [newEntries enumerateObjectsUsingBlock:^(ZZArchiveEntry *anEntry, NSUInteger index, BOOL* stop) { - - [newEntryWriters addObject:[anEntry writerCanSkipLocalFile:(idx < skipIndex)]]; + [newEntryWriters addObject:[anEntry writerCanSkipLocalFile:index < skipIndex]]; }]; // clear entries + content @@ -195,59 +195,98 @@ - (void)setEntries:(NSArray*)newEntries // skip the initial matching entries uint32_t initialSkip = skipIndex > 0 ? [[newEntryWriters objectAtIndex:skipIndex - 1] offsetToLocalFileEnd] : 0; + NSError* __autoreleasing underlyingError; + // create a temp channel for all output - id temporaryChannel = [_channel temporaryChannel]; - id temporaryChannelOutput = [temporaryChannel openOutputWithOffsetBias:initialSkip]; - - - // write out local files, recording which are valid - NSMutableIndexSet* goodEntries = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(0, newEntries.count)]; - - [newEntryWriters enumerateObjectsUsingBlock:^(id entryWriter, NSUInteger idx, BOOL *stop) - { - if (idx >= skipIndex && ![entryWriter writeLocalFileToChannelOutput:temporaryChannelOutput]) - [goodEntries removeIndex:idx]; - }]; - - ZZEndOfCentralDirectory endOfCentralDirectory; - endOfCentralDirectory.signature = ZZEndOfCentralDirectory::sign; - endOfCentralDirectory.numberOfThisDisk - = endOfCentralDirectory.numberOfTheDiskWithTheStartOfTheCentralDirectory - = 0; - endOfCentralDirectory.totalNumberOfEntriesInTheCentralDirectoryOnThisDisk - = endOfCentralDirectory.totalNumberOfEntriesInTheCentralDirectory - = goodEntries.count; - endOfCentralDirectory.offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = temporaryChannelOutput.offset; - - // write out central file headers - [newEntryWriters enumerateObjectsAtIndexes:goodEntries options:0 usingBlock:^(id anEntryWriter, NSUInteger idx, BOOL *stop) - { - [anEntryWriter writeCentralFileHeaderToChannelOutput:temporaryChannelOutput]; - }]; - - endOfCentralDirectory.sizeOfTheCentralDirectory = temporaryChannelOutput.offset - - endOfCentralDirectory.offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber; - endOfCentralDirectory.zipFileCommentLength = 0; + id temporaryChannel = [_channel temporaryChannel:&underlyingError]; + if (!temporaryChannel) + return ZZRaiseError(error, ZZOpenWriteErrorCode, @{NSUnderlyingErrorKey : underlyingError}); - // write out the end of central directory - [temporaryChannelOutput write:[NSData dataWithBytesNoCopy:&endOfCentralDirectory - length:sizeof(endOfCentralDirectory) - freeWhenDone:NO]]; - [temporaryChannelOutput close]; - - if (initialSkip) + @try { - // something skipped, append the temporary channel contents at the skipped offset - id channelOutput = [_channel openOutputWithOffsetBias:0]; - channelOutput.offset = initialSkip; - [channelOutput write:[temporaryChannel openInput:nil]]; - [channelOutput close]; + // open the channel + id temporaryChannelOutput = [temporaryChannel openOutputWithOffsetBias:initialSkip + error:&underlyingError]; + if (!temporaryChannelOutput) + return ZZRaiseError(error, ZZOpenWriteErrorCode, @{NSUnderlyingErrorKey : underlyingError}); + + @try + { + // write out local files + for (NSUInteger index = skipIndex; index < newEntriesCount; ++index) + if (![[newEntryWriters objectAtIndex:index] writeLocalFileToChannelOutput:temporaryChannelOutput + error:&underlyingError]) + return ZZRaiseError(error, ZZLocalFileWriteErrorCode, @{NSUnderlyingErrorKey : underlyingError, ZZEntryIndexKey : @(index)}); + + ZZEndOfCentralDirectory endOfCentralDirectory; + endOfCentralDirectory.signature = ZZEndOfCentralDirectory::sign; + endOfCentralDirectory.numberOfThisDisk + = endOfCentralDirectory.numberOfTheDiskWithTheStartOfTheCentralDirectory + = 0; + endOfCentralDirectory.totalNumberOfEntriesInTheCentralDirectoryOnThisDisk + = endOfCentralDirectory.totalNumberOfEntriesInTheCentralDirectory + = newEntriesCount; + endOfCentralDirectory.offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = [temporaryChannelOutput offset]; + + // write out central file headers + for (NSUInteger index = 0; index < newEntriesCount; ++index) + if (![[newEntryWriters objectAtIndex:index] writeCentralFileHeaderToChannelOutput:temporaryChannelOutput + error:&underlyingError]) + return ZZRaiseError(error, ZZCentralFileHeaderWriteErrorCode, @{NSUnderlyingErrorKey : underlyingError, ZZEntryIndexKey : @(index)}); + + endOfCentralDirectory.sizeOfTheCentralDirectory = [temporaryChannelOutput offset] + - endOfCentralDirectory.offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber; + endOfCentralDirectory.zipFileCommentLength = 0; + + // write out the end of central directory + if (![temporaryChannelOutput writeData:[NSData dataWithBytesNoCopy:&endOfCentralDirectory + length:sizeof(endOfCentralDirectory) + freeWhenDone:NO] + error:&underlyingError]) + return ZZRaiseError(error, ZZEndOfCentralDirectoryWriteErrorCode, @{NSUnderlyingErrorKey : underlyingError}); + } + @finally + { + [temporaryChannelOutput close]; + } + + if (initialSkip) + { + // something skipped, append the temporary channel contents at the skipped offset + id channelOutput = [_channel openOutputWithOffsetBias:0 + error:&underlyingError]; + if (!channelOutput) + return ZZRaiseError(error, ZZReplaceWriteErrorCode, @{NSUnderlyingErrorKey : underlyingError}); + + @try + { + NSData* channelInput = [temporaryChannel openInput:&underlyingError]; + if (!channelInput + || ![channelOutput seekToOffset:initialSkip + error:&underlyingError] + || ![channelOutput writeData:channelInput + error:&underlyingError] + || ![channelOutput truncateAtOffset:[channelOutput offset] + error:&underlyingError]) + return ZZRaiseError(error, ZZReplaceWriteErrorCode, @{NSUnderlyingErrorKey : underlyingError}); + } + @finally + { + [channelOutput close]; + } + } + else + // nothing skipped, temporary channel is entire contents: simply replace the original + if (![_channel replaceWithChannel:temporaryChannel + error:&underlyingError]) + return ZZRaiseError(error, ZZReplaceWriteErrorCode, @{NSUnderlyingErrorKey : underlyingError}); + } + @finally + { + [_channel removeTemporaries]; } - else - // nothing skipped, temporary channel is entire contents: simply replace the original - [_channel replaceWithChannel:temporaryChannel]; - [_channel removeTemporaries]; + return YES; } @end diff --git a/zipzap/ZZArchiveEntry.h b/zipzap/ZZArchiveEntry.h index bb91cbe9..c692251c 100644 --- a/zipzap/ZZArchiveEntry.h +++ b/zipzap/ZZArchiveEntry.h @@ -90,7 +90,7 @@ */ + (instancetype)archiveEntryWithFileName:(NSString*)fileName compress:(BOOL)compress - streamBlock:(BOOL(^)(NSOutputStream* stream))streamBlock; + streamBlock:(BOOL(^)(NSOutputStream* stream, NSError** error))streamBlock; /** * Creates a new file entry from a data callback. @@ -102,7 +102,7 @@ */ + (instancetype)archiveEntryWithFileName:(NSString*)fileName compress:(BOOL)compress - dataBlock:(NSData*(^)())dataBlock; + dataBlock:(NSData*(^)(NSError** error))dataBlock; /** * Creates a new file entry from a data-consuming callback. @@ -114,7 +114,7 @@ */ + (instancetype)archiveEntryWithFileName:(NSString*)fileName compress:(BOOL)compress - dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer))dataConsumerBlock; + dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer, NSError** error))dataConsumerBlock; /** * Creates a new directory entry. @@ -142,9 +142,9 @@ fileMode:(mode_t)fileMode lastModified:(NSDate*)lastModified compressionLevel:(NSInteger)compressionLevel - dataBlock:(NSData*(^)())dataBlock - streamBlock:(BOOL(^)(NSOutputStream* stream))streamBlock - dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer))dataConsumerBlock; + dataBlock:(NSData*(^)(NSError** error))dataBlock + streamBlock:(BOOL(^)(NSOutputStream* stream, NSError** error))streamBlock + dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer, NSError** error))dataConsumerBlock; /** * Checks whether the entry file is consistent. diff --git a/zipzap/ZZArchiveEntry.m b/zipzap/ZZArchiveEntry.m index 68d66628..1f9e9ad3 100644 --- a/zipzap/ZZArchiveEntry.m +++ b/zipzap/ZZArchiveEntry.m @@ -15,7 +15,7 @@ @implementation ZZArchiveEntry + (instancetype)archiveEntryWithFileName:(NSString*)fileName compress:(BOOL)compress - dataBlock:(NSData*(^)())dataBlock + dataBlock:(NSData*(^)(NSError** error))dataBlock { return [self archiveEntryWithFileName:fileName fileMode:S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH @@ -28,7 +28,7 @@ + (instancetype)archiveEntryWithFileName:(NSString*)fileName + (instancetype)archiveEntryWithFileName:(NSString*)fileName compress:(BOOL)compress - streamBlock:(BOOL(^)(NSOutputStream* stream))streamBlock + streamBlock:(BOOL(^)(NSOutputStream* stream, NSError** error))streamBlock { return [self archiveEntryWithFileName:fileName fileMode:S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH @@ -41,7 +41,7 @@ + (instancetype)archiveEntryWithFileName:(NSString*)fileName + (instancetype)archiveEntryWithFileName:(NSString*)fileName compress:(BOOL)compress - dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer))dataConsumerBlock + dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer, NSError** error))dataConsumerBlock { return [self archiveEntryWithFileName:fileName fileMode:S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH @@ -67,9 +67,9 @@ + (instancetype)archiveEntryWithFileName:(NSString*)fileName fileMode:(mode_t)fileMode lastModified:(NSDate*)lastModified compressionLevel:(NSInteger)compressionLevel - dataBlock:(NSData*(^)())dataBlock - streamBlock:(BOOL(^)(NSOutputStream* stream))streamBlock - dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer))dataConsumerBlock + dataBlock:(NSData*(^)(NSError** error))dataBlock + streamBlock:(BOOL(^)(NSOutputStream* stream, NSError** error))streamBlock + dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer, NSError** error))dataConsumerBlock { return [[ZZNewArchiveEntry alloc] initWithFileName:fileName fileMode:fileMode diff --git a/zipzap/ZZArchiveEntryWriter.h b/zipzap/ZZArchiveEntryWriter.h index 311a7c88..378f0a11 100644 --- a/zipzap/ZZArchiveEntryWriter.h +++ b/zipzap/ZZArchiveEntryWriter.h @@ -13,7 +13,8 @@ @protocol ZZArchiveEntryWriter - (uint32_t)offsetToLocalFileEnd; -- (BOOL)writeLocalFileToChannelOutput:(id)channelOutput; -- (void)writeCentralFileHeaderToChannelOutput:(id)channelOutput; - +- (BOOL)writeLocalFileToChannelOutput:(id)channelOutput + error:(NSError**)error; +- (BOOL)writeCentralFileHeaderToChannelOutput:(id)channelOutput + error:(NSError**)error; @end diff --git a/zipzap/ZZChannel.h b/zipzap/ZZChannel.h index cd13974f..7591592e 100644 --- a/zipzap/ZZChannel.h +++ b/zipzap/ZZChannel.h @@ -14,11 +14,13 @@ @property (readonly, nonatomic) NSURL* URL; -- (instancetype)temporaryChannel; -- (BOOL)replaceWithChannel:(id)channel; +- (instancetype)temporaryChannel:(NSError**)error; +- (BOOL)replaceWithChannel:(id)channel + error:(NSError**)error; - (void)removeTemporaries; - (NSData*)openInput:(NSError**)error; -- (id)openOutputWithOffsetBias:(uint32_t)offsetBias; +- (id)openOutputWithOffsetBias:(uint32_t)offsetBias + error:(NSError**)error; @end diff --git a/zipzap/ZZChannelOutput.h b/zipzap/ZZChannelOutput.h index e811ef21..45e1e235 100644 --- a/zipzap/ZZChannelOutput.h +++ b/zipzap/ZZChannelOutput.h @@ -10,9 +10,14 @@ @protocol ZZChannelOutput -@property (nonatomic) uint32_t offset; +- (uint32_t)offset; +- (BOOL)seekToOffset:(uint32_t)offset + error:(NSError**)error; -- (void)write:(NSData*)data; +- (BOOL)writeData:(NSData*)data + error:(NSError**)error; +- (BOOL)truncateAtOffset:(uint32_t)offset + error:(NSError**)error; - (void)close; @end diff --git a/zipzap/ZZDataChannel.h b/zipzap/ZZDataChannel.h index e625a60e..55082e4f 100644 --- a/zipzap/ZZDataChannel.h +++ b/zipzap/ZZDataChannel.h @@ -16,11 +16,13 @@ - (id)initWithData:(NSData*)data; -- (instancetype)temporaryChannel; -- (BOOL)replaceWithChannel:(id)channel; +- (instancetype)temporaryChannel:(NSError**)error; +- (BOOL)replaceWithChannel:(id)channel + error:(NSError**)error; - (void)removeTemporaries; - (NSData*)openInput:(NSError**)error; -- (id)openOutputWithOffsetBias:(uint32_t)offsetBias; +- (id)openOutputWithOffsetBias:(uint32_t)offsetBias + error:(NSError**)error; @end diff --git a/zipzap/ZZDataChannel.m b/zipzap/ZZDataChannel.m index 96afa898..2baa4217 100644 --- a/zipzap/ZZDataChannel.m +++ b/zipzap/ZZDataChannel.m @@ -26,12 +26,13 @@ - (NSURL*)URL return nil; } -- (instancetype)temporaryChannel +- (instancetype)temporaryChannel:(NSError**)error { return [[ZZDataChannel alloc] initWithData:[NSMutableData data]]; } - (BOOL)replaceWithChannel:(id)channel + error:(NSError**)error { [(NSMutableData*)_allData setData:((ZZDataChannel*)channel)->_allData]; return YES; @@ -47,6 +48,7 @@ - (NSData*)openInput:(NSError**)error } - (id)openOutputWithOffsetBias:(uint32_t)offsetBias + error:(NSError**)error { return [[ZZDataChannelOutput alloc] initWithData:(NSMutableData*)_allData offsetBias:offsetBias]; diff --git a/zipzap/ZZDataChannelOutput.h b/zipzap/ZZDataChannelOutput.h index 4b8c0d37..338a6c5b 100644 --- a/zipzap/ZZDataChannelOutput.h +++ b/zipzap/ZZDataChannelOutput.h @@ -12,11 +12,17 @@ @interface ZZDataChannelOutput : NSObject -@property (nonatomic) uint32_t offset; - - (id)initWithData:(NSMutableData*)data offsetBias:(uint32_t)offsetBias; -- (void)write:(NSData*)data; + +- (uint32_t)offset; +- (BOOL)seekToOffset:(uint32_t)offset + error:(NSError**)error; + +- (BOOL)writeData:(NSData*)data + error:(NSError**)error; +- (BOOL)truncateAtOffset:(uint32_t)offset + error:(NSError**)error; - (void)close; @end diff --git a/zipzap/ZZDataChannelOutput.m b/zipzap/ZZDataChannelOutput.m index e96034e0..12a1a87b 100644 --- a/zipzap/ZZDataChannelOutput.m +++ b/zipzap/ZZDataChannelOutput.m @@ -15,8 +15,6 @@ @implementation ZZDataChannelOutput uint32_t _offset; } -@synthesize offset = _offset; - - (id)initWithData:(NSMutableData*)data offsetBias:(uint32_t)offsetBias { @@ -34,12 +32,15 @@ - (uint32_t)offset return _offset + _offsetBias; } -- (void)setOffset:(uint32_t)offset +- (BOOL)seekToOffset:(uint32_t)offset + error:(NSError**)error { _offset = offset - _offsetBias; + return YES; } -- (void)write:(NSData*)data +- (BOOL)writeData:(NSData*)data + error:(NSError**)error { NSUInteger allDataLength = _allData.length; NSUInteger dataLength = data.length; @@ -57,11 +58,18 @@ - (void)write:(NSData*)data } _offset = newOffset; + return YES; +} + +- (BOOL)truncateAtOffset:(uint32_t)offset + error:(NSError**)error +{ + _allData.length = offset; + return YES; } - (void)close { - _allData.length = _offset; } @end diff --git a/zipzap/ZZDeflateOutputStream.h b/zipzap/ZZDeflateOutputStream.h index 39bd978e..473430b5 100644 --- a/zipzap/ZZDeflateOutputStream.h +++ b/zipzap/ZZDeflateOutputStream.h @@ -19,6 +19,8 @@ - (id)initWithChannelOutput:(id)channelOutput compressionLevel:(NSUInteger)compressionLevel; +- (NSError*)streamError; + - (void)open; - (void)close; diff --git a/zipzap/ZZDeflateOutputStream.m b/zipzap/ZZDeflateOutputStream.m index 9e8869f8..4d759fb9 100644 --- a/zipzap/ZZDeflateOutputStream.m +++ b/zipzap/ZZDeflateOutputStream.m @@ -17,6 +17,7 @@ @implementation ZZDeflateOutputStream { id _channelOutput; NSUInteger _compressionLevel; + NSError* _error; uint32_t _crc32; z_stream _stream; } @@ -29,8 +30,9 @@ - (id)initWithChannelOutput:(id)channelOutput compressionLevel: { _channelOutput = channelOutput; _compressionLevel = compressionLevel; - _crc32 = 0; + _error = nil; + _crc32 = 0; _stream.zalloc = Z_NULL; _stream.zfree = Z_NULL; _stream.opaque = Z_NULL; @@ -50,6 +52,11 @@ - (uint32_t)uncompressedSize return (uint32_t)_stream.total_in; } +- (NSError*)streamError +{ + return _error; +} + - (void)open { deflateInit2(&_stream, @@ -76,9 +83,14 @@ - (void)close flushing = deflate(&_stream, Z_FINISH) == Z_OK; if (_stream.avail_out < _flushLength) - [_channelOutput write:[NSData dataWithBytesNoCopy:flushBuffer - length:_flushLength - _stream.avail_out - freeWhenDone:NO]]; + { + NSError* __autoreleasing flushError; + if (![_channelOutput writeData:[NSData dataWithBytesNoCopy:flushBuffer + length:_flushLength - _stream.avail_out + freeWhenDone:NO] + error:&flushError]) + _error = flushError; + } } deflateEnd(&_stream); @@ -101,7 +113,15 @@ - (NSInteger)write:(const uint8_t*)buffer maxLength:(NSUInteger)length // write out deflated output if any outputBuffer.length = maxLength - _stream.avail_out; if (outputBuffer.length > 0) - [_channelOutput write:outputBuffer]; + { + NSError* __autoreleasing writeError; + if (![_channelOutput writeData:outputBuffer + error:&writeError]) + { + _error = writeError; + return -1; + } + } // accumulate checksum only on bytes that were deflated NSUInteger bytesWritten = length - _stream.avail_in; diff --git a/zipzap/ZZError.h b/zipzap/ZZError.h index afecc40e..ca5ffe17 100644 --- a/zipzap/ZZError.h +++ b/zipzap/ZZError.h @@ -38,11 +38,15 @@ extern NSString* const ZZEntryIndexKey; typedef enum { - ZZReadErrorCode, - ZZBadEndOfCentralDirectoryErrorCode, - ZZBadCentralFileErrorCode, - ZZBadLocalFileErrorCode, - ZZBadChecksumErrorCode + ZZOpenReadErrorCode, + ZZEndOfCentralDirectoryReadErrorCode, + ZZCentralFileHeaderReadErrorCode, + ZZLocalFileReadErrorCode, + ZZOpenWriteErrorCode, + ZZLocalFileWriteErrorCode, + ZZCentralFileHeaderWriteErrorCode, + ZZEndOfCentralDirectoryWriteErrorCode, + ZZReplaceWriteErrorCode } ZZErrorCode; static inline BOOL ZZRaiseError(NSError** error, ZZErrorCode errorCode, NSDictionary* userInfo) diff --git a/zipzap/ZZFileChannel.h b/zipzap/ZZFileChannel.h index a17f8894..6e896069 100644 --- a/zipzap/ZZFileChannel.h +++ b/zipzap/ZZFileChannel.h @@ -16,11 +16,13 @@ - (id)initWithURL:(NSURL*)URL; -- (instancetype)temporaryChannel; -- (BOOL)replaceWithChannel:(id)channel; +- (instancetype)temporaryChannel:(NSError**)error; +- (BOOL)replaceWithChannel:(id)channel + error:(NSError**)error; - (void)removeTemporaries; - (NSData*)openInput:(NSError**)error; -- (id)openOutputWithOffsetBias:(uint32_t)offsetBias; +- (id)openOutputWithOffsetBias:(uint32_t)offsetBias + error:(NSError**)error; @end diff --git a/zipzap/ZZFileChannel.m b/zipzap/ZZFileChannel.m index 2234139d..96881fd3 100644 --- a/zipzap/ZZFileChannel.m +++ b/zipzap/ZZFileChannel.m @@ -6,6 +6,7 @@ // // +#import "ZZError.h" #import "ZZFileChannel.h" #import "ZZFileChannelOutput.h" @@ -26,17 +27,19 @@ - (NSURL*)URL return _URL; } -- (instancetype)temporaryChannel +- (instancetype)temporaryChannel:(NSError**)error { NSURL* temporaryDirectory = [[NSFileManager defaultManager] URLForDirectory:NSItemReplacementDirectory inDomain:NSUserDomainMask appropriateForURL:_URL - create:YES - error:nil]; + create:NO + error:error]; + return temporaryDirectory ? [[ZZFileChannel alloc] initWithURL:[temporaryDirectory URLByAppendingPathComponent:_URL.lastPathComponent]] : nil; } - (BOOL)replaceWithChannel:(id)channel + error:(NSError**)error { NSURL* __autoreleasing resultingURL; return [[NSFileManager defaultManager] replaceItemAtURL:_URL @@ -44,8 +47,7 @@ - (BOOL)replaceWithChannel:(id)channel backupItemName:nil options:0 resultingItemURL:&resultingURL - error:nil] - && [_URL isEqual:resultingURL]; + error:error]; } - (void)removeTemporaries @@ -68,9 +70,22 @@ - (NSData*)openInput:(NSError**)error } - (id)openOutputWithOffsetBias:(uint32_t)offsetBias + error:(NSError**)error { - return [[ZZFileChannelOutput alloc] initWithURL:_URL - offsetBias:offsetBias]; + int fileDescriptor = open(_URL.path.fileSystemRepresentation, + O_WRONLY | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fileDescriptor == -1) + { + if (error) + *error = [NSError errorWithDomain:NSPOSIXErrorDomain + code:errno + userInfo:nil]; + return nil; + } + else + return [[ZZFileChannelOutput alloc] initWithFileDescriptor:fileDescriptor + offsetBias:offsetBias]; } @end diff --git a/zipzap/ZZFileChannelOutput.h b/zipzap/ZZFileChannelOutput.h index d59090b8..382d7f89 100644 --- a/zipzap/ZZFileChannelOutput.h +++ b/zipzap/ZZFileChannelOutput.h @@ -14,9 +14,18 @@ @property (nonatomic) uint32_t offset; -- (id)initWithURL:(NSURL*)URL - offsetBias:(uint32_t)offsetBias; -- (void)write:(NSData*)data; +- (id)initWithFileDescriptor:(int)fileDescriptor + offsetBias:(uint32_t)offsetBias; + +- (uint32_t)offset; +- (BOOL)seekToOffset:(uint32_t)offset + error:(NSError**)error; + +- (BOOL)writeData:(NSData*)data + error:(NSError**)error; +- (BOOL)truncateAtOffset:(uint32_t)offset + error:(NSError**)error; + - (void)close; @end diff --git a/zipzap/ZZFileChannelOutput.m b/zipzap/ZZFileChannelOutput.m index 0d96e7e7..5f1dd9a1 100644 --- a/zipzap/ZZFileChannelOutput.m +++ b/zipzap/ZZFileChannelOutput.m @@ -14,14 +14,12 @@ @implementation ZZFileChannelOutput uint32_t _offsetBias; } -- (id)initWithURL:(NSURL*)URL - offsetBias:(uint32_t)offsetBias +- (id)initWithFileDescriptor:(int)fileDescriptor + offsetBias:(uint32_t)offsetBias { if ((self = [super init])) { - _fileDescriptor = open(URL.path.fileSystemRepresentation, - O_WRONLY | O_CREAT, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + _fileDescriptor = fileDescriptor; _offsetBias = offsetBias; } return self; @@ -32,19 +30,48 @@ - (uint32_t)offset return (uint32_t)lseek(_fileDescriptor, 0, SEEK_CUR) + _offsetBias; } -- (void)setOffset:(uint32_t)offset +- (BOOL)seekToOffset:(uint32_t)offset + error:(NSError**)error { - lseek(_fileDescriptor, offset - _offsetBias, SEEK_SET); + if (lseek(_fileDescriptor, offset - _offsetBias, SEEK_SET) == -1) + { + if (error) + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]; + return NO; + } + else + return YES; } -- (void)write:(NSData*)data +- (BOOL)writeData:(NSData*)data + error:(NSError**)error { - write(_fileDescriptor, data.bytes, data.length); + if (write(_fileDescriptor, data.bytes, data.length) == -1) + { + if (error) + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]; + return NO; + } + else + return YES; + +} + +- (BOOL)truncateAtOffset:(uint32_t)offset + error:(NSError**)error +{ + if (ftruncate(_fileDescriptor, offset) == -1) + { + if (error) + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]; + return NO; + } + else + return YES; } - (void)close { - ftruncate(_fileDescriptor, lseek(_fileDescriptor, 0, SEEK_CUR)); close(_fileDescriptor); } diff --git a/zipzap/ZZNewArchiveEntry.h b/zipzap/ZZNewArchiveEntry.h index 19485594..1477ef2f 100644 --- a/zipzap/ZZNewArchiveEntry.h +++ b/zipzap/ZZNewArchiveEntry.h @@ -27,8 +27,8 @@ fileMode:(mode_t)fileMode lastModified:(NSDate*)lastModified compressionLevel:(NSInteger)compressionLevel - dataBlock:(NSData*(^)())dataBlock - streamBlock:(BOOL(^)(NSOutputStream* stream))streamBlock - dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer))dataConsumerBlock; + dataBlock:(NSData*(^)(NSError** error))dataBlock + streamBlock:(BOOL(^)(NSOutputStream* stream, NSError** error))streamBlock + dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer, NSError** error))dataConsumerBlock; @end diff --git a/zipzap/ZZNewArchiveEntry.mm b/zipzap/ZZNewArchiveEntry.mm index ab99bcdf..9ab8936e 100644 --- a/zipzap/ZZNewArchiveEntry.mm +++ b/zipzap/ZZNewArchiveEntry.mm @@ -15,18 +15,18 @@ @implementation ZZNewArchiveEntry mode_t _fileMode; NSDate* _lastModified; NSInteger _compressionLevel; - NSData* (^_dataBlock)(); - BOOL (^_streamBlock)(NSOutputStream* stream); - BOOL (^_dataConsumerBlock)(CGDataConsumerRef dataConsumer); + NSData* (^_dataBlock)(NSError** error); + BOOL (^_streamBlock)(NSOutputStream* stream, NSError** error); + BOOL (^_dataConsumerBlock)(CGDataConsumerRef dataConsumer, NSError** error); } - (id)initWithFileName:(NSString*)fileName fileMode:(mode_t)fileMode lastModified:(NSDate*)lastModified compressionLevel:(NSInteger)compressionLevel - dataBlock:(NSData*(^)())dataBlock - streamBlock:(BOOL(^)(NSOutputStream* stream))streamBlock - dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer))dataConsumerBlock; + dataBlock:(NSData*(^)(NSError** error))dataBlock + streamBlock:(BOOL(^)(NSOutputStream* stream, NSError** error))streamBlock + dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer, NSError** error))dataConsumerBlock; { if ((self = [super init])) { diff --git a/zipzap/ZZNewArchiveEntryWriter.h b/zipzap/ZZNewArchiveEntryWriter.h index 738053d5..4e438503 100644 --- a/zipzap/ZZNewArchiveEntryWriter.h +++ b/zipzap/ZZNewArchiveEntryWriter.h @@ -16,13 +16,15 @@ fileMode:(mode_t)fileMode lastModified:(NSDate*)lastModified compressionLevel:(NSInteger)compressionLevel - dataBlock:(NSData*(^)())dataBlock - streamBlock:(BOOL(^)(NSOutputStream* stream))streamBlock - dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer))dataConsumerBlock; + dataBlock:(NSData*(^)(NSError** error))dataBlock + streamBlock:(BOOL(^)(NSOutputStream* stream, NSError** error))streamBlock + dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer, NSError** error))dataConsumerBlock; - (uint32_t)offsetToLocalFileEnd; -- (BOOL)writeLocalFileToChannelOutput:(id)channelOutput; -- (void)writeCentralFileHeaderToChannelOutput:(id)channelOutput; +- (BOOL)writeLocalFileToChannelOutput:(id)channelOutput + error:(NSError**)error; +- (BOOL)writeCentralFileHeaderToChannelOutput:(id)channelOutput + error:(NSError**)error; @end diff --git a/zipzap/ZZNewArchiveEntryWriter.mm b/zipzap/ZZNewArchiveEntryWriter.mm index bd7d73bc..b8b6b992 100644 --- a/zipzap/ZZNewArchiveEntryWriter.mm +++ b/zipzap/ZZNewArchiveEntryWriter.mm @@ -40,18 +40,18 @@ @implementation ZZNewArchiveEntryWriter NSMutableData* _centralFileHeader; NSMutableData* _localFileHeader; NSInteger _compressionLevel; - NSData* (^_dataBlock)(); - BOOL (^_streamBlock)(NSOutputStream* stream); - BOOL (^_dataConsumerBlock)(CGDataConsumerRef dataConsumer); + NSData* (^_dataBlock)(NSError** error); + BOOL (^_streamBlock)(NSOutputStream* stream, NSError** error); + BOOL (^_dataConsumerBlock)(CGDataConsumerRef dataConsumer, NSError** error); } - (id)initWithFileName:(NSString*)fileName fileMode:(mode_t)fileMode lastModified:(NSDate*)lastModified compressionLevel:(NSInteger)compressionLevel - dataBlock:(NSData*(^)())dataBlock - streamBlock:(BOOL(^)(NSOutputStream* stream))streamBlock - dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer))dataConsumerBlock; + dataBlock:(NSData*(^)(NSError** error))dataBlock + streamBlock:(BOOL(^)(NSOutputStream* stream, NSError** error))streamBlock + dataConsumerBlock:(BOOL(^)(CGDataConsumerRef dataConsumer, NSError** error))dataConsumerBlock; { if ((self = [super init])) { @@ -172,120 +172,138 @@ - (uint32_t)offsetToLocalFileEnd } - (BOOL)writeLocalFileToChannelOutput:(id)channelOutput + error:(NSError**)error { - // free any temp objects created while writing, especially via the callbacks which we don't control - @autoreleasepool + ZZCentralFileHeader* centralFileHeader = [self centralFileHeader]; + + // save current offset, then write out all of local file to the file handle + centralFileHeader->relativeOffsetOfLocalHeader = [channelOutput offset]; + if (![channelOutput writeData:_localFileHeader + error:error]) + return NO; + + ZZDataDescriptor dataDescriptor; + dataDescriptor.signature = ZZDataDescriptor::sign; + + if (_compressionLevel) { - BOOL goodData = NO; - ZZCentralFileHeader* centralFileHeader = [self centralFileHeader]; - - // save current offset, then write out all of local file to the file handle - centralFileHeader->relativeOffsetOfLocalHeader = channelOutput.offset; - [channelOutput write:_localFileHeader]; - - ZZDataDescriptor dataDescriptor; - dataDescriptor.signature = ZZDataDescriptor::sign; - - if (_compressionLevel) + // use of one the blocks to write to a stream that deflates directly to the output file handle + ZZDeflateOutputStream* outputStream = [[ZZDeflateOutputStream alloc] initWithChannelOutput:channelOutput + compressionLevel:_compressionLevel]; + [outputStream open]; + @try { - // use of one the blocks to write to a stream that deflates directly to the output file handle - ZZDeflateOutputStream* outputStream = [[ZZDeflateOutputStream alloc] initWithChannelOutput:channelOutput - compressionLevel:_compressionLevel]; - [outputStream open]; if (_dataBlock) { - NSData* data = _dataBlock(); - goodData = data != nil; - - if (data) - { - const uint8_t* bytes; - NSUInteger bytesToWrite; - NSUInteger bytesWritten; - for (bytes = (const uint8_t*)data.bytes, bytesToWrite = data.length; - bytesToWrite > 0; - bytes += bytesWritten, bytesToWrite -= bytesWritten) - bytesWritten = [outputStream write:bytes maxLength:bytesToWrite]; - } + NSData* data = _dataBlock(error); + if (!data) + return NO; + + const uint8_t* bytes; + NSUInteger bytesToWrite; + NSUInteger bytesWritten; + for (bytes = (const uint8_t*)data.bytes, bytesToWrite = data.length; + bytesToWrite > 0; + bytes += bytesWritten, bytesToWrite -= bytesWritten) + bytesWritten = [outputStream write:bytes maxLength:bytesToWrite]; } else if (_streamBlock) - goodData = _streamBlock(outputStream); + { + if (!_streamBlock(outputStream, error)) + return NO; + } else if (_dataConsumerBlock) { CGDataConsumerRef dataConsumer = CGDataConsumerCreate((__bridge void*)outputStream, &ZZDataConsumer::callbacks); - goodData = _dataConsumerBlock(dataConsumer); - CGDataConsumerRelease(dataConsumer); + @try + { + if (!_dataConsumerBlock(dataConsumer, error)) + return NO; + } + @finally + { + CGDataConsumerRelease(dataConsumer); + } } - else - goodData = YES; - + } + @finally + { [outputStream close]; + } + + dataDescriptor.crc32 = outputStream.crc32; + dataDescriptor.compressedSize = outputStream.compressedSize; + dataDescriptor.uncompressedSize = outputStream.uncompressedSize; + } + else + { + if (_dataBlock) + { + // if data block, write the data directly to output file handle + NSData* data = _dataBlock(error); + if (!data + || ![channelOutput writeData:data error:error]) + return NO; - dataDescriptor.crc32 = outputStream.crc32; - dataDescriptor.compressedSize = outputStream.compressedSize; - dataDescriptor.uncompressedSize = outputStream.uncompressedSize; + dataDescriptor.compressedSize = dataDescriptor.uncompressedSize = (uint32_t)data.length; + dataDescriptor.crc32 = (uint32_t)crc32(0, (const Bytef*)data.bytes, dataDescriptor.uncompressedSize); } else { - if (_dataBlock) - { - // if data block, write the data directly to output file handle - NSData* data = _dataBlock(); - goodData = data != nil; - - if (data) - [channelOutput write:data]; - - dataDescriptor.compressedSize = dataDescriptor.uncompressedSize = (uint32_t)data.length; - dataDescriptor.crc32 = (uint32_t)crc32(0, (const Bytef*)data.bytes, dataDescriptor.uncompressedSize); - } - else + // if stream block, data consumer block or no block, use to write to a stream that just outputs to the output file handle + ZZStoreOutputStream* outputStream = [[ZZStoreOutputStream alloc] initWithChannelOutput:channelOutput]; + [outputStream open]; + + @try { - // if stream block, data consumer block or no block, use to write to a stream that just outputs to the output file handle - ZZStoreOutputStream* outputStream = [[ZZStoreOutputStream alloc] initWithChannelOutput:channelOutput]; - [outputStream open]; - if (_streamBlock) - goodData = _streamBlock(outputStream); + { + if (!_streamBlock(outputStream, error)) + return NO; + } else if (_dataConsumerBlock) { CGDataConsumerRef dataConsumer = CGDataConsumerCreate((__bridge void*)outputStream, &ZZDataConsumer::callbacks); - goodData = _dataConsumerBlock(dataConsumer); - CGDataConsumerRelease(dataConsumer); + @try + { + if (!_dataConsumerBlock(dataConsumer, error)) + return NO; + } + @finally + { + CGDataConsumerRelease(dataConsumer); + } } - else - goodData = YES; - + } + @finally + { [outputStream close]; - - dataDescriptor.crc32 = outputStream.crc32; - dataDescriptor.compressedSize = dataDescriptor.uncompressedSize = outputStream.size; } - } - - if (goodData) - { - // save the crc32, compressedSize, uncompressedSize, then write out the data descriptor - centralFileHeader->crc32 = dataDescriptor.crc32; - centralFileHeader->compressedSize = dataDescriptor.compressedSize; - centralFileHeader->uncompressedSize = dataDescriptor.uncompressedSize; - [channelOutput write:[NSData dataWithBytesNoCopy:&dataDescriptor - length:sizeof(dataDescriptor) - freeWhenDone:NO]]; - return YES; - } - else - { - // callback had erred: rewind the file handle - channelOutput.offset = centralFileHeader->relativeOffsetOfLocalHeader; - return NO; + + dataDescriptor.crc32 = outputStream.crc32; + dataDescriptor.compressedSize = dataDescriptor.uncompressedSize = outputStream.size; } } + + // save the crc32, compressedSize, uncompressedSize, then write out the data descriptor + centralFileHeader->crc32 = dataDescriptor.crc32; + centralFileHeader->compressedSize = dataDescriptor.compressedSize; + centralFileHeader->uncompressedSize = dataDescriptor.uncompressedSize; + if (![channelOutput writeData:[NSData dataWithBytesNoCopy:&dataDescriptor + length:sizeof(dataDescriptor) + freeWhenDone:NO] + error:error]) + return NO; + + return YES; } -- (void)writeCentralFileHeaderToChannelOutput:(id)channelOutput +- (BOOL)writeCentralFileHeaderToChannelOutput:(id)channelOutput + error:(NSError**)error + { - [channelOutput write:_centralFileHeader]; + return [channelOutput writeData:_centralFileHeader + error:error]; } @end diff --git a/zipzap/ZZOldArchiveEntry.mm b/zipzap/ZZOldArchiveEntry.mm index fe72cca1..bb9b7c1e 100644 --- a/zipzap/ZZOldArchiveEntry.mm +++ b/zipzap/ZZOldArchiveEntry.mm @@ -180,11 +180,9 @@ - (BOOL)check:(NSError**)error || dataDescriptorSignature != ZZDataDescriptor::sign || localCrc32 != _centralFileHeader->crc32 || localCompressedSize != _centralFileHeader->compressedSize - || localUncompressedSize != _centralFileHeader->uncompressedSize) - ZZRaiseError(error, ZZBadLocalFileErrorCode, nil); - - if (_localFileHeader->crc32 != (uint32_t)crc32(0, _localFileHeader->fileData(), (uInt)_localFileHeader->compressedSize)) - ZZRaiseError(error, ZZBadChecksumErrorCode, nil); + || localUncompressedSize != _centralFileHeader->uncompressedSize + || _localFileHeader->crc32 != (uint32_t)crc32(0, _localFileHeader->fileData(), (uInt)_localFileHeader->compressedSize)) + ZZRaiseError(error, ZZLocalFileReadErrorCode, nil); return YES; } diff --git a/zipzap/ZZOldArchiveEntryWriter.h b/zipzap/ZZOldArchiveEntryWriter.h index 8034da79..790fb84d 100644 --- a/zipzap/ZZOldArchiveEntryWriter.h +++ b/zipzap/ZZOldArchiveEntryWriter.h @@ -17,7 +17,10 @@ shouldSkipLocalFile:(BOOL)shouldSkipLocalFile; - (uint32_t)offsetToLocalFileEnd; -- (BOOL)writeLocalFileToChannelOutput:(id)channelOutput; -- (void)writeCentralFileHeaderToChannelOutput:(id)channelOutput; +- (BOOL)writeLocalFileToChannelOutput:(id)channelOutput + error:(NSError**)error; +- (BOOL)writeCentralFileHeaderToChannelOutput:(id)channelOutput + error:(NSError**)error; + @end diff --git a/zipzap/ZZOldArchiveEntryWriter.mm b/zipzap/ZZOldArchiveEntryWriter.mm index 8c8563e9..165c6eb7 100644 --- a/zipzap/ZZOldArchiveEntryWriter.mm +++ b/zipzap/ZZOldArchiveEntryWriter.mm @@ -56,20 +56,24 @@ - (uint32_t)offsetToLocalFileEnd } - (BOOL)writeLocalFileToChannelOutput:(id)channelOutput + error:(NSError**)error { if (_localFile) { // can't skip: save the offset, then write out the local file bytes - [self centralFileHeader]->relativeOffsetOfLocalHeader = channelOutput.offset; - [channelOutput write:_localFile]; + [self centralFileHeader]->relativeOffsetOfLocalHeader = [channelOutput offset]; + return [channelOutput writeData:_localFile + error:error]; } - - return YES; + else + return YES; } -- (void)writeCentralFileHeaderToChannelOutput:(id)channelOutput +- (BOOL)writeCentralFileHeaderToChannelOutput:(id)channelOutput + error:(NSError**)error { - [channelOutput write:_centralFileHeader]; + return [channelOutput writeData:_centralFileHeader + error:error]; } @end diff --git a/zipzap/ZZStoreOutputStream.h b/zipzap/ZZStoreOutputStream.h index 106ded9e..68ede7e5 100644 --- a/zipzap/ZZStoreOutputStream.h +++ b/zipzap/ZZStoreOutputStream.h @@ -20,6 +20,8 @@ - (void)open; - (void)close; +- (NSError*)streamError; + - (NSInteger)write:(const uint8_t*)buffer maxLength:(NSUInteger)length; - (BOOL)hasSpaceAvailable; diff --git a/zipzap/ZZStoreOutputStream.m b/zipzap/ZZStoreOutputStream.m index a67ba02c..9c94dc02 100644 --- a/zipzap/ZZStoreOutputStream.m +++ b/zipzap/ZZStoreOutputStream.m @@ -14,6 +14,7 @@ @implementation ZZStoreOutputStream { id _channelOutput; + NSError* _error; uint32_t _crc32; uint32_t _size; } @@ -26,12 +27,19 @@ - (id)initWithChannelOutput:(id)channelOutput if ((self = [super init])) { _channelOutput = channelOutput; + + _error = nil; _crc32 = 0; _size = 0; } return self; } +- (NSError*)streamError +{ + return _error; +} + - (void)open { } @@ -42,9 +50,15 @@ - (void)close - (NSInteger)write:(const uint8_t*)buffer maxLength:(NSUInteger)length { - [_channelOutput write:[NSData dataWithBytesNoCopy:(void*)buffer - length:length - freeWhenDone:NO]]; + NSError* __autoreleasing writeError; + if (![_channelOutput writeData:[NSData dataWithBytesNoCopy:(void*)buffer + length:length + freeWhenDone:NO] + error:&writeError]) + { + _error = writeError; + return -1; + } // accumulate checksum and size from written bytes _crc32 = (uint32_t)crc32(_crc32, buffer, (uInt)length); diff --git a/zipzapTests/ZZZipTests.h b/zipzapTests/ZZZipTests.h index d100679f..a06b3063 100644 --- a/zipzapTests/ZZZipTests.h +++ b/zipzapTests/ZZZipTests.h @@ -44,6 +44,15 @@ - (void)testRemovingFileZipEntryAtBack; - (void)testRemovingFileZipEntryAtMiddle; +- (void)testInsertingFileZipEntryWithCompressedBadData; +- (void)testInsertingFileZipEntryWithUncompressedBadData; +- (void)testInsertingFileZipEntryWithCompressedBadStreamWriteNone; +- (void)testInsertingFileZipEntryWithUncompressedBadStreamWriteNone; +- (void)testInsertingFileZipEntryWithCompressedBadStreamWriteSome; +- (void)testInsertingFileZipEntryWithUncompressedBadStreamWriteSome; +- (void)testInsertingFileZipEntryWithCompressedBadDataConsumerWriteNone; +- (void)testInsertingFileZipEntryWithUncompressedBadDataConsumerWriteNone; + - (void)testCreatingDataZipWithNoEntries; - (void)testCreatingDataZipEntriesWithDirectory; @@ -75,4 +84,13 @@ - (void)testRemovingDataZipEntryAtBack; - (void)testRemovingDataZipEntryAtMiddle; +- (void)testInsertingDataZipEntryWithCompressedBadData; +- (void)testInsertingDataZipEntryWithUncompressedBadData; +- (void)testInsertingDataZipEntryWithCompressedBadStreamWriteNone; +- (void)testInsertingDataZipEntryWithUncompressedBadStreamWriteNone; +- (void)testInsertingDataZipEntryWithCompressedBadStreamWriteSome; +- (void)testInsertingDataZipEntryWithUncompressedBadStreamWriteSome; +- (void)testInsertingDataZipEntryWithCompressedBadDataConsumerWriteNone; +- (void)testInsertingDataZipEntryWithUncompressedBadDataConsumerWriteNone; + @end diff --git a/zipzapTests/ZZZipTests.m b/zipzapTests/ZZZipTests.m index e83a0df7..053bc8b1 100644 --- a/zipzapTests/ZZZipTests.m +++ b/zipzapTests/ZZZipTests.m @@ -13,6 +13,8 @@ @interface ZZZipTests () +- (NSError*)someError; + - (NSData*)dataAtFilePath:(NSString*)filePath; - (void)createEmptyFileZip; - (void)createEmptyDataZip; @@ -27,11 +29,13 @@ - (void)checkCreatingZipEntriesWithNoCheckEntries:(NSArray*)entries; - (void)checkCreatingZipEntriesWithDataCompressed:(BOOL)compressed; - (void)checkCreatingZipEntriesWithStreamCompressed:(BOOL)compressed chunkSize:(NSUInteger)chunkSize; - (void)checkCreatingZipEntriesWithImageCompressed:(BOOL)compressed; -- (void)checkCreatingZipEntriesWithBadEntry:(ZZArchiveEntry*)badEntry; - (void)checkUpdatingZipEntriesWithFile:(NSString*)file operationBlock:(void(^)(NSMutableArray*, id))operationBlock; +- (void)checkCreatingZipEntriesToUpdate:(BOOL)update + withBadEntry:(ZZArchiveEntry*)badEntry; + @end @implementation ZZZipTests @@ -42,6 +46,11 @@ @implementation ZZZipTests ZZMutableArchive* _zipFile; } +- (NSError*)someError +{ + return [NSError errorWithDomain:@"com.pixelglow.zipzap.something" code:99 userInfo:nil]; +} + - (void)setUp { _zipFileURL = [NSURL fileURLWithPath:@"/tmp/test.zip"]; @@ -56,9 +65,6 @@ - (void)setUp - (void)tearDown { - STAssertTrue([ZZTasks testZipAtPath:_zipFileURL.path], - @"zipFile must pass unzip test."); - _zipFile = nil; [[NSFileManager defaultManager] removeItemAtURL:_zipFileURL error:nil]; @@ -213,11 +219,13 @@ - (void)checkZipEntryRecords:(NSArray*)newEntryRecords index); } } + + STAssertTrue([ZZTasks testZipAtPath:_zipFileURL.path], @"zipFile must pass unzip test."); } - (void)checkCreatingZipEntriesWithNoCheckEntries:(NSArray*)entries { - _zipFile.entries = entries; + STAssertTrue([_zipFile updateEntries:entries error:nil], @"Updating entries should succeed."); [self checkZipEntryRecords:[self recordsForZipEntries:entries] checkerBlock:nil]; @@ -229,9 +237,9 @@ - (void)checkCreatingZipEntriesWithDataCompressed:(BOOL)compressed for (NSString* entryFilePath in _entryFilePaths) [newEntries addObject:[ZZArchiveEntry archiveEntryWithFileName:entryFilePath compress:compressed - dataBlock:^{ return [self dataAtFilePath:entryFilePath]; }]]; + dataBlock:^(NSError** error){ return [self dataAtFilePath:entryFilePath]; }]]; - _zipFile.entries = newEntries; + STAssertTrue([_zipFile updateEntries:newEntries error:nil], @"Updating entries should succeed."); [self checkZipEntryRecords:[self recordsForZipEntries:newEntries] checkerBlock:^(NSString* fileName){ return [self dataAtFilePath:fileName]; }]; } @@ -242,22 +250,29 @@ - (void)checkCreatingZipEntriesWithStreamCompressed:(BOOL)compressed chunkSize:( for (NSString* entryFilePath in _entryFilePaths) [newEntries addObject:[ZZArchiveEntry archiveEntryWithFileName:entryFilePath compress:compressed - streamBlock:^(NSOutputStream* outputStream) + streamBlock:^(NSOutputStream* outputStream, NSError** error) { NSData* data = [self dataAtFilePath:entryFilePath]; const uint8_t* bytes; - NSUInteger bytesLeft; - NSUInteger bytesWritten; + NSInteger bytesLeft; + NSInteger bytesWritten; for (bytes = (const uint8_t*)data.bytes, bytesLeft = data.length; bytesLeft > 0; bytes += bytesWritten, bytesLeft -= bytesWritten) + { bytesWritten = [outputStream write:bytes maxLength:MIN(bytesLeft, chunkSize)]; - + if (bytesWritten == -1) + { + if (error) + *error = outputStream.streamError; + return NO; + } + } return YES; }]]; - _zipFile.entries = newEntries; + STAssertTrue([_zipFile updateEntries:newEntries error:nil], @"Updating entries should succeed."); [self checkZipEntryRecords:[self recordsForZipEntries:newEntries] checkerBlock:^(NSString* fileName){ return [self dataAtFilePath:fileName]; }]; } @@ -274,7 +289,7 @@ - (void)checkCreatingZipEntriesWithImageCompressed:(BOOL)compressed [newEntries addObject:[ZZArchiveEntry archiveEntryWithFileName:entryFilePath compress:compressed - dataConsumerBlock:^(CGDataConsumerRef dataConsumer) + dataConsumerBlock:^(CGDataConsumerRef dataConsumer, NSError** error) { CGImageSourceRef fileImageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)[[NSBundle bundleForClass:self.class] URLForResource:entryFilePath withExtension:nil], @@ -324,29 +339,11 @@ - (void)checkCreatingZipEntriesWithImageCompressed:(BOOL)compressed }]]; } - _zipFile.entries = newEntries; + STAssertTrue([_zipFile updateEntries:newEntries error:nil], @"Updating entries should succeed."); [self checkZipEntryRecords:[self recordsForZipEntries:newEntries] checkerBlock:^(NSString* fileName){ return fileNameCheck[fileName]; }]; } -- (void)checkCreatingZipEntriesWithBadEntry:(ZZArchiveEntry*)badEntry -{ - NSMutableArray* newEntries = [NSMutableArray array]; - for (NSString* entryFilePath in _entryFilePaths) - [newEntries addObject:[ZZArchiveEntry archiveEntryWithFileName:entryFilePath - compress:YES - dataBlock:^{ return [self dataAtFilePath:entryFilePath]; }]]; - - NSUInteger insertIndex = newEntries.count / 2; - [newEntries insertObject:badEntry atIndex:insertIndex]; - _zipFile.entries = newEntries; - [newEntries removeObjectAtIndex:insertIndex]; - - [self checkZipEntryRecords:[self recordsForZipEntries:newEntries] - checkerBlock:^(NSString* fileName){ return [self dataAtFilePath:fileName]; }]; - -} - - (void)checkUpdatingZipEntriesWithFile:(NSString*)file operationBlock:(void(^)(NSMutableArray*, id))operationBlock { @@ -354,15 +351,59 @@ - (void)checkUpdatingZipEntriesWithFile:(NSString*)file operationBlock(entries, file ? [ZZArchiveEntry archiveEntryWithFileName:file compress:YES - dataBlock:^{ return [self dataAtFilePath:file]; }] : nil); + dataBlock:^(NSError** error){ return [self dataAtFilePath:file]; }] : nil); // since entries contains existing entries from the zip file, we need to take a record first before applying it NSArray* records = [self recordsForZipEntries:entries]; - _zipFile.entries = entries; + STAssertTrue([_zipFile updateEntries:entries error:nil], @"Updating entries should succeed."); [self checkZipEntryRecords:records checkerBlock:^(NSString* fileName){ return [self dataAtFilePath:fileName]; }]; } +- (void)checkCreatingZipEntriesToUpdate:(BOOL)update withBadEntry:(ZZArchiveEntry*)badEntry +{ + NSMutableArray* newEntries = [NSMutableArray array]; + for (NSString* entryFilePath in _entryFilePaths) + [newEntries addObject:[ZZArchiveEntry archiveEntryWithFileName:entryFilePath + compress:YES + dataBlock:^(NSError** error){ return [self dataAtFilePath:entryFilePath]; }]]; + + if (update) + { + STAssertTrue([_zipFile updateEntries:newEntries error:nil], @"Updating entries should succeed."); + newEntries = [NSMutableArray arrayWithArray:_zipFile.entries]; + } + + NSUInteger insertIndex = newEntries.count / 2; + [newEntries insertObject:badEntry atIndex:insertIndex]; + + NSError* __autoreleasing error; + STAssertFalse([_zipFile updateEntries:newEntries error:&error], + @"Updating entries with bad entry should fail."); + STAssertNotNil(error, + @"Error object should be set."); + STAssertEqualObjects(error.domain, + ZZErrorDomain, + @"Error domain should be in zipzap."); + STAssertEquals((ZZErrorCode)error.code, + ZZLocalFileWriteErrorCode, + @"Error code should be bad local file write."); + STAssertEquals([error.userInfo[ZZEntryIndexKey] unsignedIntegerValue], + insertIndex, + @"Error entry index should be bad entry index."); + STAssertEqualObjects(error.userInfo[NSUnderlyingErrorKey], + [self someError], + @"Error underlying error should be same as passed-in error."); + + if (update) + { + if (!_zipFile.URL) + // archive is a data archive, need to save it before we can check + [_zipFile.contents writeToURL:_zipFileURL atomically:YES]; + STAssertTrue([ZZTasks testZipAtPath:_zipFileURL.path], @"zipFile must pass unzip test."); + } +} + - (void)testCreatingFileZipWithNoEntries { [self createEmptyFileZip]; @@ -430,90 +471,119 @@ - (void)testCreatingFileZipEntriesWithUncompressedImage - (void)testCreatingFileZipEntriesWithCompressedBadData { [self createEmptyFileZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" - compress:YES - dataBlock:^ { return (NSData*)nil; }]]; + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + dataBlock:^(NSError** error) + { + if (error) + *error = [self someError]; + return (NSData*)nil; + }]]; } - (void)testCreatingFileZipEntriesWithUncompressedBadData { [self createEmptyFileZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" - compress:YES - dataBlock:^ { return (NSData*)nil; }]]; + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + dataBlock:^(NSError** error) + { + if (error) + *error = [self someError]; + return (NSData*)nil; + }]]; } - (void)testCreatingFileZipEntriesWithCompressedBadStreamWriteNone { [self createEmptyFileZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" - compress:YES - streamBlock:^(NSOutputStream* stream) - { - return NO; - }]]; + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + streamBlock:^(NSOutputStream* stream, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; } - (void)testCreatingFileZipEntriesWithUncompressedBadStreamWriteNone { [self createEmptyFileZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" - compress:NO - streamBlock:^(NSOutputStream* stream) - { - return NO; - }]]; + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:NO + streamBlock:^(NSOutputStream* stream, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; } - (void)testCreatingFileZipEntriesWithCompressedBadStreamWriteSome { [self createEmptyFileZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" - compress:YES - streamBlock:^(NSOutputStream* stream) - { - uint8_t buffer[1024]; - [stream write:buffer maxLength:sizeof(buffer)]; - return NO; - }]]; + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + streamBlock:^(NSOutputStream* stream, NSError** error) + { + uint8_t buffer[1024]; + [stream write:buffer maxLength:sizeof(buffer)]; + if (error) + *error = [self someError]; + return NO; + }]]; } - (void)testCreatingFileZipEntriesWithUncompressedBadStreamWriteSome { [self createEmptyFileZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" - compress:NO - streamBlock:^(NSOutputStream* stream) - { - uint8_t buffer[1024]; - [stream write:buffer maxLength:sizeof(buffer)]; - return NO; - }]]; + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:NO + streamBlock:^(NSOutputStream* stream, NSError** error) + { + uint8_t buffer[1024]; + [stream write:buffer maxLength:sizeof(buffer)]; + if (error) + *error = [self someError]; + return NO; + }]]; } - (void)testCreatingFileZipEntriesWithCompressedBadDataConsumerWriteNone { [self createEmptyFileZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" - compress:YES - dataConsumerBlock:^(CGDataConsumerRef dataConsumer) - { - return NO; - }]]; + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + dataConsumerBlock:^(CGDataConsumerRef dataConsumer, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; } - (void)testCreatingFileZipEntriesWithUncompressedBadDataConsumerWriteNone { [self createEmptyFileZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" - compress:NO - dataConsumerBlock:^(CGDataConsumerRef dataConsumer) - { - return NO; - }]]; + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:NO + dataConsumerBlock:^(CGDataConsumerRef dataConsumer, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; } - - (void)testInsertingFileZipEntryAtFront { [self createFullFileZip]; @@ -577,6 +647,122 @@ - (void)testRemovingFileZipEntryAtMiddle operationBlock:^(NSMutableArray* entries, id entry) { [entries removeObjectAtIndex:entries.count / 2]; }]; } +- (void)testInsertingFileZipEntryWithCompressedBadData +{ + [self createFullFileZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + dataBlock:^(NSError** error) + { + if (error) + *error = [self someError]; + return (NSData*)nil; + }]]; +} + +- (void)testInsertingFileZipEntryWithUncompressedBadData +{ + [self createFullFileZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + dataBlock:^(NSError** error) + { + if (error) + *error = [self someError]; + return (NSData*)nil; + }]]; +} + +- (void)testInsertingFileZipEntryWithCompressedBadStreamWriteNone +{ + [self createFullFileZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + streamBlock:^(NSOutputStream* stream, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; +} + +- (void)testInsertingFileZipEntryWithUncompressedBadStreamWriteNone +{ + [self createFullFileZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:NO + streamBlock:^(NSOutputStream* stream, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; +} + +- (void)testInsertingFileZipEntryWithCompressedBadStreamWriteSome +{ + [self createFullFileZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + streamBlock:^(NSOutputStream* stream, NSError** error) + { + uint8_t buffer[1024]; + [stream write:buffer maxLength:sizeof(buffer)]; + if (error) + *error = [self someError]; + return NO; + }]]; +} + +- (void)testInsertingFileZipEntryWithUncompressedBadStreamWriteSome +{ + [self createFullFileZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:NO + streamBlock:^(NSOutputStream* stream, NSError** error) + { + uint8_t buffer[1024]; + [stream write:buffer maxLength:sizeof(buffer)]; + if (error) + *error = [self someError]; + return NO; + }]]; +} + +- (void)testInsertingFileZipEntryWithCompressedBadDataConsumerWriteNone +{ + [self createFullFileZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + dataConsumerBlock:^(CGDataConsumerRef dataConsumer, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; +} + +- (void)testInsertingFileZipEntryWithUncompressedBadDataConsumerWriteNone +{ + [self createFullFileZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:NO + dataConsumerBlock:^(CGDataConsumerRef dataConsumer, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; +} + - (void)testCreatingDataZipWithNoEntries { [self createEmptyDataZip]; @@ -644,87 +830,118 @@ - (void)testCreatingDataZipEntriesWithUncompressedImage - (void)testCreatingDataZipEntriesWithCompressedBadData { [self createEmptyDataZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" compress:YES - dataBlock:^ { return (NSData*)nil; }]]; + dataBlock:^(NSError** error) + { + if (error) + *error = [self someError]; + NSLog(@"%@", *error); + return (NSData*)nil; + }]]; } - (void)testCreatingDataZipEntriesWithUncompressedBadData { [self createEmptyDataZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" - compress:YES - dataBlock:^ { return (NSData*)nil; }]]; + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + dataBlock:^(NSError** error) + { + if (error) + *error = [self someError]; + return (NSData*)nil; + }]]; } - (void)testCreatingDataZipEntriesWithCompressedBadStreamWriteNone { [self createEmptyDataZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" - compress:YES - streamBlock:^(NSOutputStream* stream) - { - return NO; - }]]; + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + streamBlock:^(NSOutputStream* stream, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; } - (void)testCreatingDataZipEntriesWithUncompressedBadStreamWriteNone { [self createEmptyDataZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" - compress:NO - streamBlock:^(NSOutputStream* stream) - { - return NO; - }]]; + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:NO + streamBlock:^(NSOutputStream* stream, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; } - (void)testCreatingDataZipEntriesWithCompressedBadStreamWriteSome { [self createEmptyDataZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" - compress:YES - streamBlock:^(NSOutputStream* stream) - { - uint8_t buffer[1024]; - [stream write:buffer maxLength:sizeof(buffer)]; - return NO; - }]]; + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + streamBlock:^(NSOutputStream* stream, NSError** error) + { + uint8_t buffer[1024]; + [stream write:buffer maxLength:sizeof(buffer)]; + if (error) + *error = [self someError]; + return NO; + }]]; } - (void)testCreatingDataZipEntriesWithUncompressedBadStreamWriteSome { [self createEmptyDataZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" - compress:NO - streamBlock:^(NSOutputStream* stream) - { - uint8_t buffer[1024]; - [stream write:buffer maxLength:sizeof(buffer)]; - return NO; - }]]; + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:NO + streamBlock:^(NSOutputStream* stream, NSError** error) + { + uint8_t buffer[1024]; + [stream write:buffer maxLength:sizeof(buffer)]; + if (error) + *error = [self someError]; + return NO; + }]]; } - (void)testCreatingDataZipEntriesWithCompressedBadDataConsumerWriteNone { [self createEmptyDataZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" - compress:YES - dataConsumerBlock:^(CGDataConsumerRef dataConsumer) - { - return NO; - }]]; + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + dataConsumerBlock:^(CGDataConsumerRef dataConsumer, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; } - (void)testCreatingDataZipEntriesWithUncompressedBadDataConsumerWriteNone { [self createEmptyDataZip]; - [self checkCreatingZipEntriesWithBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" - compress:NO - dataConsumerBlock:^(CGDataConsumerRef dataConsumer) - { - return NO; - }]]; + [self checkCreatingZipEntriesToUpdate:NO + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:NO + dataConsumerBlock:^(CGDataConsumerRef dataConsumer, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; } @@ -791,5 +1008,119 @@ - (void)testRemovingDataZipEntryAtMiddle operationBlock:^(NSMutableArray* entries, id entry) { [entries removeObjectAtIndex:entries.count / 2]; }]; } +- (void)testInsertingDataZipEntryWithCompressedBadData +{ + [self createFullDataZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + dataBlock:^(NSError** error) + { + if (error) + *error = [self someError]; + return (NSData*)nil; + }]]; +} + +- (void)testInsertingDataZipEntryWithUncompressedBadData +{ + [self createFullDataZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + dataBlock:^(NSError** error) + { + if (error) + *error = [self someError]; + return (NSData*)nil; + }]]; +} + +- (void)testInsertingDataZipEntryWithCompressedBadStreamWriteNone +{ + [self createFullDataZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + streamBlock:^(NSOutputStream* stream, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; +} + +- (void)testInsertingDataZipEntryWithUncompressedBadStreamWriteNone +{ + [self createFullDataZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:NO + streamBlock:^(NSOutputStream* stream, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; +} + +- (void)testInsertingDataZipEntryWithCompressedBadStreamWriteSome +{ + [self createFullDataZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + streamBlock:^(NSOutputStream* stream, NSError** error) + { + uint8_t buffer[1024]; + [stream write:buffer maxLength:sizeof(buffer)]; + if (error) + *error = [self someError]; + return NO; + }]]; +} +- (void)testInsertingDataZipEntryWithUncompressedBadStreamWriteSome +{ + [self createFullDataZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:NO + streamBlock:^(NSOutputStream* stream, NSError** error) + { + uint8_t buffer[1024]; + [stream write:buffer maxLength:sizeof(buffer)]; + if (error) + *error = [self someError]; + return NO; + }]]; +} + +- (void)testInsertingDataZipEntryWithCompressedBadDataConsumerWriteNone +{ + [self createFullDataZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:YES + dataConsumerBlock:^(CGDataConsumerRef dataConsumer, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; +} + +- (void)testInsertingDataZipEntryWithUncompressedBadDataConsumerWriteNone +{ + [self createFullDataZip]; + [self checkCreatingZipEntriesToUpdate:YES + withBadEntry:[ZZArchiveEntry archiveEntryWithFileName:@"bad" + compress:NO + dataConsumerBlock:^(CGDataConsumerRef dataConsumer, NSError** error) + { + if (error) + *error = [self someError]; + return NO; + }]]; +} @end