Index: src/OFZIPArchive.m ================================================================== --- src/OFZIPArchive.m +++ src/OFZIPArchive.m @@ -45,13 +45,11 @@ #import "OFUnsupportedVersionException.h" /* * FIXME: Current limitations: * - Split archives are not supported. - * - Write support is missing. * - Encrypted files cannot be read. - * - No support for writing ZIP64 files. */ @interface OFZIPArchive () - (void)of_readZIPInfo; - (void)of_readEntries; @@ -473,16 +471,16 @@ } - (OFStream *)streamForWritingEntry: (OFZIPArchiveEntry *)entry_ { /* TODO: Avoid data descriptor when _stream is an OFSeekableStream */ + int64_t offsetAdd = 0; void *pool; OFMutableZIPArchiveEntry *entry; OFString *fileName; OFData *extraField; uint16_t fileNameLength, extraFieldLength; - int64_t offsetAdd = 0; if (_mode != OF_ZIP_ARCHIVE_MODE_WRITE && _mode != OF_ZIP_ARCHIVE_MODE_APPEND) @throw [OFInvalidArgumentException exception]; @@ -500,11 +498,16 @@ @throw [OFNotImplementedException exceptionWithSelector: _cmd object: self]; [self of_closeLastReturnedStream]; - if (_offset > UINT32_MAX) + fileName = [entry fileName]; + fileNameLength = [fileName UTF8StringLength]; + extraField = [entry extraField]; + extraFieldLength = [extraField count]; + + if (UINT16_MAX - extraFieldLength < 20) @throw [OFOutOfRangeException exception]; [entry setVersionMadeBy: ([entry versionMadeBy] & 0xFF00) | 45]; [entry setMinVersionNeeded: ([entry minVersionNeeded] & 0xFF00) | 45]; [entry setCompressedSize: 0]; @@ -518,29 +521,32 @@ [_stream writeLittleEndianInt16: [entry minVersionNeeded]]; [_stream writeLittleEndianInt16: [entry generalPurposeBitFlag]]; [_stream writeLittleEndianInt16: [entry compressionMethod]]; [_stream writeLittleEndianInt16: [entry of_lastModifiedFileTime]]; [_stream writeLittleEndianInt16: [entry of_lastModifiedFileDate]]; - /* We use the data descriptor */ - [_stream writeLittleEndianInt32: 0]; - [_stream writeLittleEndianInt32: 0]; - [_stream writeLittleEndianInt32: 0]; - offsetAdd += 4 + (5 * 2) + (3 * 4); - - fileName = [entry fileName]; - fileNameLength = [fileName UTF8StringLength]; - extraField = [entry extraField]; - extraFieldLength = [extraField count]; - + /* We use ZIP64 */ + [_stream writeLittleEndianInt32: 0xFFFFFFFF]; + [_stream writeLittleEndianInt32: 0xFFFFFFFF]; + [_stream writeLittleEndianInt32: 0xFFFFFFFF]; [_stream writeLittleEndianInt16: fileNameLength]; - [_stream writeLittleEndianInt16: extraFieldLength]; - offsetAdd += 2 * 2; + [_stream writeLittleEndianInt16: extraFieldLength + 20]; + offsetAdd += 4 + (5 * 2) + (3 * 4) + (2 * 2); [_stream writeString: fileName]; + offsetAdd += fileNameLength; + + [_stream writeLittleEndianInt16: + OF_ZIP_ARCHIVE_ENTRY_EXTRA_FIELD_ZIP64]; + [_stream writeLittleEndianInt16: 16]; + /* We use the data descriptor */ + [_stream writeLittleEndianInt64: 0]; + [_stream writeLittleEndianInt64: 0]; + offsetAdd += (2 * 2) + (2 * 8); + if (extraField != nil) [_stream writeData: extraField]; - offsetAdd += fileNameLength + extraFieldLength; + offsetAdd += extraFieldLength; if (INT64_MAX - _offset < offsetAdd) @throw [OFOutOfRangeException exception]; _offset += offsetAdd; @@ -556,13 +562,10 @@ - (void)of_writeCentralDirectory { void *pool = objc_autoreleasePoolPush(); - if (_offset > UINT32_MAX) - @throw [OFOutOfRangeException exception]; - _centralDirectoryEntries = 0; _centralDirectoryEntriesInDisk = 0; _centralDirectorySize = 0; _centralDirectoryOffset = _offset; @@ -570,20 +573,37 @@ _centralDirectorySize += [entry of_writeToStream: _stream]; _centralDirectoryEntries++; _centralDirectoryEntriesInDisk++; } - if (_centralDirectorySize > UINT32_MAX) - @throw [OFOutOfRangeException exception]; + /* ZIP64 end of central directory */ + [_stream writeLittleEndianInt32: 0x06064B50]; + [_stream writeLittleEndianInt64: 44]; /* Remaining size */ + [_stream writeLittleEndianInt16: 45]; /* Version made by */ + [_stream writeLittleEndianInt16: 45]; /* Version required */ + [_stream writeLittleEndianInt32: _diskNumber]; + [_stream writeLittleEndianInt32: _centralDirectoryDisk]; + [_stream writeLittleEndianInt64: _centralDirectoryEntriesInDisk]; + [_stream writeLittleEndianInt64: _centralDirectoryEntries]; + [_stream writeLittleEndianInt64: _centralDirectorySize]; + [_stream writeLittleEndianInt64: _centralDirectoryOffset]; + + /* ZIP64 end of central directory locator */ + [_stream writeLittleEndianInt32: 0x07064B50]; + [_stream writeLittleEndianInt32: _diskNumber]; + [_stream writeLittleEndianInt64: + _centralDirectoryOffset + _centralDirectorySize]; + [_stream writeLittleEndianInt32: 1]; /* Total number of disks */ + /* End of central directory */ [_stream writeLittleEndianInt32: 0x06054B50]; - [_stream writeLittleEndianInt16: _diskNumber]; - [_stream writeLittleEndianInt16: _centralDirectoryDisk]; - [_stream writeLittleEndianInt16: _centralDirectoryEntriesInDisk]; - [_stream writeLittleEndianInt16: _centralDirectoryEntries]; - [_stream writeLittleEndianInt32: (uint32_t)_centralDirectorySize]; - [_stream writeLittleEndianInt32: (uint32_t)_centralDirectoryOffset]; + [_stream writeLittleEndianInt16: 0xFFFF]; /* Disk number */ + [_stream writeLittleEndianInt16: 0xFFFF]; /* CD disk */ + [_stream writeLittleEndianInt16: 0xFFFF]; /* CD entries in disk */ + [_stream writeLittleEndianInt16: 0xFFFF]; /* CD entries */ + [_stream writeLittleEndianInt32: 0xFFFFFFFF]; /* CD size */ + [_stream writeLittleEndianInt32: 0xFFFFFFFF]; /* CD offset */ [_stream writeLittleEndianInt16: [_archiveComment UTF8StringLength]]; if (_archiveComment != nil) [_stream writeString: _archiveComment]; objc_autoreleasePoolPop(pool); @@ -835,30 +855,24 @@ _CRC32 = of_crc32(_CRC32, buffer, length); } - (void)close { - uint32_t bytesWritten; - if (_stream == nil) return; - if (_bytesWritten > UINT32_MAX) - @throw [OFOutOfRangeException exception]; - - bytesWritten = (uint32_t)_bytesWritten; - [_stream writeLittleEndianInt32: 0x08074B50]; [_stream writeLittleEndianInt32: _CRC32]; - [_stream writeLittleEndianInt32: bytesWritten]; - [_stream writeLittleEndianInt32: bytesWritten]; - _bytesWritten += (4 * 4); + [_stream writeLittleEndianInt64: _bytesWritten]; + [_stream writeLittleEndianInt64: _bytesWritten]; [_stream release]; _stream = nil; [_entry setCRC32: ~_CRC32]; - [_entry setCompressedSize: bytesWritten]; - [_entry setUncompressedSize: bytesWritten]; + [_entry setCompressedSize: _bytesWritten]; + [_entry setUncompressedSize: _bytesWritten]; [_entry makeImmutable]; + + _bytesWritten += (2 * 4 + 2 * 8); } @end Index: src/OFZIPArchiveEntry.m ================================================================== --- src/OFZIPArchiveEntry.m +++ src/OFZIPArchiveEntry.m @@ -422,49 +422,55 @@ return [ret autorelease]; } - (uint64_t)of_writeToStream: (OFStream *)stream { - void *pool; + void *pool = objc_autoreleasePoolPush(); uint64_t size = 0; - if (_compressedSize > UINT32_MAX || - _uncompressedSize > UINT32_MAX || - _localFileHeaderOffset > UINT32_MAX) + if (UINT16_MAX - [_extraField count] < 32) @throw [OFOutOfRangeException exception]; - pool = objc_autoreleasePoolPush(); - [stream writeLittleEndianInt32: 0x02014B50]; [stream writeLittleEndianInt16: _versionMadeBy]; [stream writeLittleEndianInt16: _minVersionNeeded]; [stream writeLittleEndianInt16: _generalPurposeBitFlag]; [stream writeLittleEndianInt16: _compressionMethod]; [stream writeLittleEndianInt16: _lastModifiedFileTime]; [stream writeLittleEndianInt16: _lastModifiedFileDate]; [stream writeLittleEndianInt32: _CRC32]; - [stream writeLittleEndianInt32: (uint32_t)_compressedSize]; - [stream writeLittleEndianInt32: (uint32_t)_uncompressedSize]; + [stream writeLittleEndianInt32: 0xFFFFFFFF]; + [stream writeLittleEndianInt32: 0xFFFFFFFF]; [stream writeLittleEndianInt16: (uint16_t)[_fileName UTF8StringLength]]; - [stream writeLittleEndianInt16: (uint16_t)[_extraField count]]; + [stream writeLittleEndianInt16: (uint16_t)[_extraField count] + 32]; [stream writeLittleEndianInt16: (uint16_t)[_fileComment UTF8StringLength]]; - [stream writeLittleEndianInt16: _startDiskNumber]; + [stream writeLittleEndianInt16: 0xFFFF]; [stream writeLittleEndianInt16: _internalAttributes]; [stream writeLittleEndianInt32: _versionSpecificAttributes]; - [stream writeLittleEndianInt32: (uint32_t)_localFileHeaderOffset]; + [stream writeLittleEndianInt32: 0xFFFFFFFF]; size += (4 + (6 * 2) + (3 * 4) + (5 * 2) + (2 * 4)); [stream writeString: _fileName]; + size += (uint64_t)[_fileName UTF8StringLength]; + + [stream writeLittleEndianInt16: OF_ZIP_ARCHIVE_ENTRY_EXTRA_FIELD_ZIP64]; + [stream writeLittleEndianInt16: 28]; + [stream writeLittleEndianInt64: _uncompressedSize]; + [stream writeLittleEndianInt64: _compressedSize]; + [stream writeLittleEndianInt64: _localFileHeaderOffset]; + [stream writeLittleEndianInt32: _startDiskNumber]; + size += (2 * 2) + (3 * 8) + 4; + if (_extraField != nil) [stream writeData: _extraField]; + size += (uint64_t)[_extraField count]; + if (_fileComment != nil) [stream writeString: _fileComment]; - size += (uint64_t)[_fileName UTF8StringLength] + - (uint64_t)[_extraField count] + - (uint64_t)[_fileComment UTF8StringLength]; + size += (uint64_t)[_fileComment UTF8StringLength]; objc_autoreleasePoolPop(pool); return size; } @end