Index: src/OFLHAArchive.m ================================================================== --- src/OFLHAArchive.m +++ src/OFLHAArchive.m @@ -69,11 +69,11 @@ OFLHAArchive *_archive; OFMutableLHAArchiveEntry *_entry; OFStringEncoding _encoding; OFSeekableStream *_stream; OFStreamOffset _headerOffset; - uint32_t _bytesWritten; + uint64_t _bytesWritten; uint16_t _CRC16; } - (instancetype)of_initWithArchive: (OFLHAArchive *)archive stream: (OFSeekableStream *)stream @@ -552,28 +552,28 @@ - (size_t)lowlevelWriteBuffer: (const void *)buffer length: (size_t)length { if (_stream == nil) @throw [OFNotOpenException exceptionWithObject: self]; - if (UINT32_MAX - _bytesWritten < length) + if (UINT64_MAX - _bytesWritten < length) @throw [OFOutOfRangeException exception]; @try { [_stream writeBuffer: buffer length: length]; } @catch (OFWriteFailedException *e) { OFEnsure(e.bytesWritten <= length); - _bytesWritten += (uint32_t)e.bytesWritten; + _bytesWritten += (uint64_t)e.bytesWritten; _CRC16 = OFCRC16(_CRC16, buffer, e.bytesWritten); if (e.errNo == EWOULDBLOCK || e.errNo == EAGAIN) return e.bytesWritten; @throw e; } - _bytesWritten += (uint32_t)length; + _bytesWritten += (uint64_t)length; _CRC16 = OFCRC16(_CRC16, buffer, length); return length; } Index: src/OFLHAArchiveEntry.m ================================================================== --- src/OFLHAArchiveEntry.m +++ src/OFLHAArchiveEntry.m @@ -198,10 +198,26 @@ entry->_modificationDate = nil; entry->_modificationDate = [[OFDate alloc] initWithTimeIntervalSince1970: modificationDate]; } + +static void +parseFileSizeExtension(OFLHAArchiveEntry *entry, OFData *extension, + OFStringEncoding encoding) +{ + uint64_t tmp; + + if (extension.count != 17) + @throw [OFInvalidFormatException exception]; + + memcpy(&tmp, (char *)extension.items + 1, 8); + entry->_compressedSize = OFFromLittleEndian64(tmp); + + memcpy(&tmp, (char *)extension.items + 9, 8); + entry->_uncompressedSize = OFFromLittleEndian64(tmp); +} static bool parseExtension(OFLHAArchiveEntry *entry, OFData *extension, OFStringEncoding encoding, bool allowFileName) { @@ -217,10 +233,13 @@ function = parseDirectoryNameExtension; break; case 0x3F: function = parseCommentExtension; break; + case 0x42: + function = parseFileSizeExtension; + break; case 0x50: function = parsePermissionsExtension; break; case 0x51: function = parseGIDUIDExtension; @@ -599,10 +618,11 @@ OFMutableData *data = [OFMutableData dataWithCapacity: 24]; const char *fileName, *directoryName; size_t fileNameLength, directoryNameLength; uint16_t tmp16; uint32_t tmp32; + uint64_t tmp64; size_t headerSize; if ([_compressionMethod cStringLengthWithEncoding: OFStringEncodingASCII] != 5) @throw [OFInvalidArgumentException exception]; @@ -610,11 +630,11 @@ getFileNameAndDirectoryName(self, encoding, &fileName, &fileNameLength, &directoryName, &directoryNameLength); if (fileNameLength > UINT16_MAX - 3 || directoryNameLength > UINT16_MAX - 3 || - _compressedSize > UINT32_MAX || _uncompressedSize > UINT32_MAX) + _compressedSize > UINT64_MAX || _uncompressedSize > UINT64_MAX) @throw [OFOutOfRangeException exception]; /* Length. Filled in after we're done. */ [data increaseCountBy: 2]; @@ -675,10 +695,24 @@ [data addItem: "\x3F"]; [data addItems: [_fileComment cStringWithEncoding: encoding] count: fileCommentLength]; } + /* + * Always include the file size extension, as the header can be written + * with size 0 initially and then rewritten with the actual size in + * case the data to be archived is being streamed - but for that we + * need to make sure we always have the space. + */ + tmp16 = OFToLittleEndian16(19); + [data addItems: &tmp16 count: sizeof(tmp16)]; + [data addItem: "\x42"]; + tmp64 = OFToLittleEndian64(_compressedSize); + [data addItems: &tmp64 count: sizeof(tmp64)]; + tmp64 = OFToLittleEndian64(_uncompressedSize); + [data addItems: &tmp64 count: sizeof(tmp64)]; + if (_POSIXPermissions != nil) { tmp16 = OFToLittleEndian16(5); [data addItems: &tmp16 count: sizeof(tmp16)]; [data addItem: "\x50"];