Zip64 support added to Poco Zip. I added unit tests CompressTest::testZip64 and ZipTest::testDecompressZip64, but so far I have only run them on Windows. We have built the code on MAC and Linux and will attempt to run the tests there.

This commit is contained in:
bobstabler 2015-04-22 11:40:34 -05:00
parent b565d72d4c
commit 5080f1aafa
20 changed files with 924 additions and 144 deletions

View File

@ -38,9 +38,11 @@ class Zip_API Compress
public: public:
Poco::FIFOEvent<const ZipLocalFileHeader> EDone; Poco::FIFOEvent<const ZipLocalFileHeader> EDone;
Compress(std::ostream& out, bool seekableOut); Compress(std::ostream& out, bool seekableOut, bool forceZip64 = false);
/// seekableOut determines how we write the zip, setting it to true is recommended for local files (smaller zip file), /// seekableOut determines how we write the zip, setting it to true is recommended for local files (smaller zip file),
/// if you are compressing directly to a network, you MUST set it to false /// if you are compressing directly to a network, you MUST set it to false
/// If forceZip64 is set true then the file header is allocated with zip64 extension so that it can be updated after the file data is written
/// if seekableOut is true in case the compressed or uncompressed size exceeds 32 bits.
~Compress(); ~Compress();
@ -103,16 +105,18 @@ private:
/// for directories. /// for directories.
void addFileRaw(std::istream& in, const ZipLocalFileHeader& hdr, const Poco::Path& fileName); void addFileRaw(std::istream& in, const ZipLocalFileHeader& hdr, const Poco::Path& fileName);
/// Copies an already compressed ZipEntry from in /// copys an already compressed ZipEntry from in
private: private:
std::set<std::string> _storeExtensions; std::set<std::string> _storeExtensions;
std::ostream& _out; std::ostream& _out;
bool _seekableOut; bool _seekableOut;
bool _forceZip64;
ZipArchive::FileHeaders _files; ZipArchive::FileHeaders _files;
ZipArchive::FileInfos _infos; ZipArchive::FileInfos _infos;
ZipArchive::DirectoryInfos _dirs; ZipArchive::DirectoryInfos _dirs;
Poco::UInt32 _offset; ZipArchive::DirectoryInfos64 _dirs64;
Poco::UInt64 _offset;
std::string _comment; std::string _comment;
friend class Keep; friend class Keep;

View File

@ -24,6 +24,7 @@
#include "Poco/Zip/ZipLocalFileHeader.h" #include "Poco/Zip/ZipLocalFileHeader.h"
#include "Poco/Zip/ZipFileInfo.h" #include "Poco/Zip/ZipFileInfo.h"
#include "Poco/Zip/ZipArchiveInfo.h" #include "Poco/Zip/ZipArchiveInfo.h"
#include <istream> #include <istream>
#include <map> #include <map>
@ -43,6 +44,7 @@ public:
typedef std::map<std::string, ZipLocalFileHeader> FileHeaders; typedef std::map<std::string, ZipLocalFileHeader> FileHeaders;
typedef std::map<std::string, ZipFileInfo> FileInfos; typedef std::map<std::string, ZipFileInfo> FileInfos;
typedef std::map<Poco::UInt16, ZipArchiveInfo> DirectoryInfos; typedef std::map<Poco::UInt16, ZipArchiveInfo> DirectoryInfos;
typedef std::map<Poco::UInt32, ZipArchiveInfo64> DirectoryInfos64;
ZipArchive(std::istream& in); ZipArchive(std::istream& in);
/// Creates the ZipArchive from a file. Note that the in stream will be in state failed after the constructor is finished /// Creates the ZipArchive from a file. Note that the in stream will be in state failed after the constructor is finished
@ -68,7 +70,7 @@ public:
private: private:
void parse(std::istream& in, ParseCallback& pc); void parse(std::istream& in, ParseCallback& pc);
ZipArchive(const FileHeaders& entries, const FileInfos& infos, const DirectoryInfos& dirs ); ZipArchive(const FileHeaders& entries, const FileInfos& infos, const DirectoryInfos& dirs, const DirectoryInfos64& dirs64 );
private: private:
FileHeaders _entries; FileHeaders _entries;
@ -77,6 +79,8 @@ private:
/// Info generated by parsing the directory block of the zip file /// Info generated by parsing the directory block of the zip file
DirectoryInfos _disks; DirectoryInfos _disks;
/// Stores directory info for all found disks /// Stores directory info for all found disks
DirectoryInfos64 _disks64;
/// Stores directory info for all found disks
friend class Compress; friend class Compress;
}; };

View File

@ -70,16 +70,20 @@ public:
/// Sets the optional Zip comment. /// Sets the optional Zip comment.
void setNumberOfEntries(Poco::UInt16 val); void setNumberOfEntries(Poco::UInt16 val);
/// Returns the number of entries on this disk /// Sets the number of entries on this disk
void setTotalNumberOfEntries(Poco::UInt16 val); void setTotalNumberOfEntries(Poco::UInt16 val);
/// Returns the total number of entries on all disks /// Sets the total number of entries on all disks
void setCentralDirectorySize(Poco::UInt32 val); void setCentralDirectorySize(Poco::UInt32 val);
/// Returns the size of the central directory in bytes /// Sets the size of the central directory in bytes
void setHeaderOffset(Poco::UInt32 val); void setCentralDirectoryOffset(Poco::UInt32 val);
/// Sets the offset of the central directory from beginning of first disk
void setHeaderOffset(std::streamoff val);
/// Sets the offset of the header in relation to the begin of this disk
std::string createHeader() const; std::string createHeader() const;
/// Creates a header /// Creates a header
@ -181,11 +185,210 @@ inline void ZipArchiveInfo::setCentralDirectorySize(Poco::UInt32 val)
} }
inline void ZipArchiveInfo::setHeaderOffset(Poco::UInt32 val) inline void ZipArchiveInfo::setCentralDirectoryOffset(Poco::UInt32 val)
{ {
ZipUtil::set32BitValue(val, _rawInfo, CENTRALDIRSTARTOFFSET_POS); ZipUtil::set32BitValue(val, _rawInfo, CENTRALDIRSTARTOFFSET_POS);
} }
inline void ZipArchiveInfo::setHeaderOffset(std::streamoff val)
{
_startPos = val;
}
class Zip_API ZipArchiveInfo64
/// A ZipArchiveInfo64 stores central directory info
{
public:
static const char HEADER[ZipCommon::HEADER_SIZE];
static const char LOCATOR_HEADER[ZipCommon::HEADER_SIZE];
ZipArchiveInfo64();
/// Default constructor, everything set to zero or empty
ZipArchiveInfo64(std::istream& in, bool assumeHeaderRead);
/// Creates the ZipArchiveInfo64 by parsing the input stream.
/// If assumeHeaderRead is true we assume that the first 4 bytes were already read outside.
~ZipArchiveInfo64();
/// Destroys the ZipArchiveInfo64.
void getVersionMadeBy(int& major, int& minor);
/// The ZIP version used to create the file
void getRequiredVersion(int& major, int& minor);
/// The minimum version required to extract the data
Poco::UInt32 getDiskNumber() const;
/// Get the number of the disk where this header can be found
Poco::UInt32 getFirstDiskForDirectoryHeader() const;
/// Returns the number of the disk that contains the start of the directory header
Poco::UInt64 getNumberOfEntries() const;
/// Returns the number of entries on this disk
Poco::UInt64 getTotalNumberOfEntries() const;
/// Returns the total number of entries on all disks
Poco::UInt64 getCentralDirectorySize() const;
/// Returns the size of the central directory in bytes
std::streamoff getCentralDirectoryOffset() const;
/// Returns the offset of the central directory from beginning of first disk
std::streamoff getHeaderOffset() const;
/// Returns the offset of the header in relation to the begin of this disk
void setNumberOfEntries(Poco::UInt64 val);
/// Sets the number of entries on this disk
void setTotalNumberOfEntries(Poco::UInt64 val);
/// Sets the total number of entries on all disks
void setCentralDirectorySize(Poco::UInt64 val);
/// Set the size of the central directory in bytes
void setCentralDirectoryOffset(Poco::UInt64 val);
/// Returns the offset of the central directory from beginning of first disk
void setHeaderOffset(std::streamoff val);
/// Sets the offset of the header in relation to the begin of this disk
void setTotalNumberOfDisks(Poco::UInt32 val);
/// Sets the offset of the central directory from beginning of first disk
std::string createHeader() const;
/// Creates a header
private:
void parse(std::istream& inp, bool assumeHeaderRead);
void setRequiredVersion(int major, int minor);
private:
enum
{
HEADER_POS = 0,
RECORDSIZE_POS = HEADER_POS + ZipCommon::HEADER_SIZE,
RECORDSIZE_SIZE = 8,
VERSIONMADEBY_POS = RECORDSIZE_POS + RECORDSIZE_SIZE,
VERSIONMADEBY_SIZE = 2,
VERSION_NEEDED_POS = VERSIONMADEBY_POS + VERSIONMADEBY_SIZE,
VERSION_NEEDED_SIZE = 2,
NUMBEROFTHISDISK_POS = VERSION_NEEDED_POS + VERSION_NEEDED_SIZE,
NUMBEROFTHISDISK_SIZE = 4,
NUMBEROFCENTRALDIRDISK_POS = NUMBEROFTHISDISK_POS + NUMBEROFTHISDISK_SIZE,
NUMBEROFCENTRALDIRDISK_SIZE = 4,
NUMENTRIESTHISDISK_POS = NUMBEROFCENTRALDIRDISK_POS + NUMBEROFCENTRALDIRDISK_SIZE,
NUMENTRIESTHISDISK_SIZE = 8,
TOTALNUMENTRIES_POS = NUMENTRIESTHISDISK_POS + NUMENTRIESTHISDISK_SIZE,
TOTALNUMENTRIES_SIZE = 8,
CENTRALDIRSIZE_POS = TOTALNUMENTRIES_POS + TOTALNUMENTRIES_SIZE,
CENTRALDIRSIZE_SIZE = 8,
CENTRALDIRSTARTOFFSET_POS = CENTRALDIRSIZE_POS + CENTRALDIRSIZE_SIZE,
CENTRALDIRSTARTOFFSET_SIZE = 8,
FULL_HEADER_SIZE = 56,
LOCATOR_HEADER_POS = 0,
NUMBEROFENDOFCENTRALDIRDISK_POS = LOCATOR_HEADER_POS + ZipCommon::HEADER_SIZE,
NUMBEROFENDOFCENTRALDIRDISK_SIZE = 4,
ENDOFCENTRALDIROFFSET_POS = NUMBEROFENDOFCENTRALDIRDISK_POS + NUMBEROFENDOFCENTRALDIRDISK_SIZE,
ENDOFCENTRALDIROFFSET_SIZE = 8,
TOTALNUMBEROFENDDISKS_POS = ENDOFCENTRALDIROFFSET_POS + ENDOFCENTRALDIROFFSET_SIZE,
TOTALNUMBEROFENDDISKS_SIZE = 4,
FULL_LOCATOR_SIZE = 20
};
char _rawInfo[FULL_HEADER_SIZE];
std::string _extraField;
char _locInfo[FULL_LOCATOR_SIZE];
std::streamoff _startPos;
};
inline Poco::UInt32 ZipArchiveInfo64::getDiskNumber() const
{
return ZipUtil::get32BitValue(_rawInfo, NUMBEROFTHISDISK_POS);
}
inline Poco::UInt32 ZipArchiveInfo64::getFirstDiskForDirectoryHeader() const
{
return ZipUtil::get32BitValue(_rawInfo, NUMBEROFCENTRALDIRDISK_POS);
}
inline Poco::UInt64 ZipArchiveInfo64::getNumberOfEntries() const
{
return ZipUtil::get64BitValue(_rawInfo, NUMENTRIESTHISDISK_POS);
}
inline Poco::UInt64 ZipArchiveInfo64::getTotalNumberOfEntries() const
{
return ZipUtil::get64BitValue(_rawInfo, TOTALNUMENTRIES_POS);
}
inline Poco::UInt64 ZipArchiveInfo64::getCentralDirectorySize() const
{
return ZipUtil::get64BitValue(_rawInfo, CENTRALDIRSIZE_POS);
}
inline std::streamoff ZipArchiveInfo64::getCentralDirectoryOffset() const
{
return _startPos;
}
inline std::streamoff ZipArchiveInfo64::getHeaderOffset() const
{
return _startPos;
}
inline void ZipArchiveInfo64::setRequiredVersion(int major, int minor)
{
poco_assert (minor < 10);
poco_assert (major < 24);
Poco::UInt8 val = static_cast<unsigned char>(major)*10+static_cast<unsigned char>(minor);
_rawInfo[VERSIONMADEBY_POS] = static_cast<char>(val);
_rawInfo[VERSION_NEEDED_POS] = static_cast<char>(val);
}
inline void ZipArchiveInfo64::setNumberOfEntries(Poco::UInt64 val)
{
ZipUtil::set64BitValue(val, _rawInfo, NUMENTRIESTHISDISK_POS);
}
inline void ZipArchiveInfo64::setTotalNumberOfEntries(Poco::UInt64 val)
{
ZipUtil::set64BitValue(val, _rawInfo, TOTALNUMENTRIES_POS);
}
inline void ZipArchiveInfo64::setCentralDirectorySize(Poco::UInt64 val)
{
ZipUtil::set64BitValue(val, _rawInfo, CENTRALDIRSIZE_POS);
}
inline void ZipArchiveInfo64::setCentralDirectoryOffset(Poco::UInt64 val)
{
ZipUtil::set64BitValue(val, _rawInfo, CENTRALDIRSTARTOFFSET_POS);
}
inline void ZipArchiveInfo64::setHeaderOffset(std::streamoff val)
{
_startPos = val;
ZipUtil::set64BitValue(val, _locInfo, ENDOFCENTRALDIROFFSET_POS);
}
inline void ZipArchiveInfo64::setTotalNumberOfDisks(Poco::UInt32 val)
{
ZipUtil::set32BitValue(val, _locInfo, TOTALNUMBEROFENDDISKS_POS);
}
} } // namespace Poco::Zip } } // namespace Poco::Zip

View File

@ -33,8 +33,12 @@ class Zip_API ZipCommon
public: public:
enum enum
{ {
HEADER_SIZE = 4 HEADER_SIZE = 4,
}; };
static const Poco::UInt16 ZIP64_EXTRA_ID = 0x1; // Extra data id tag for Zip64 data (in extension for ZipLocalFileHeader and ZipFileInfo)
static const Poco::UInt16 ZIP64_MAGIC_SHORT = 0xFFFF;
static const Poco::UInt32 ZIP64_MAGIC = 0xFFFFFFFF;
enum CompressionMethod enum CompressionMethod
{ {

View File

@ -134,7 +134,110 @@ inline Poco::UInt32 ZipDataInfo::getFullHeaderSize()
} }
class Zip_API ZipDataInfo64
/// A ZipDataInfo64 stores a Zip data descriptor for a Zip64 file
{
public:
static const char HEADER[ZipCommon::HEADER_SIZE];
ZipDataInfo64();
/// Creates a header with all fields (except the header field) set to 0
ZipDataInfo64(std::istream& in, bool assumeHeaderRead);
/// Creates the ZipDataInfo64.
~ZipDataInfo64();
/// Destroys the ZipDataInfo64.
bool isValid() const;
Poco::UInt32 getCRC32() const;
void setCRC32(Poco::UInt32 crc);
Poco::UInt64 getCompressedSize() const;
void setCompressedSize(Poco::UInt64 size);
Poco::UInt64 getUncompressedSize() const;
void setUncompressedSize(Poco::UInt64 size);
static Poco::UInt32 getFullHeaderSize();
const char* getRawHeader() const;
private:
enum
{
HEADER_POS = 0,
CRC32_POS = HEADER_POS + ZipCommon::HEADER_SIZE,
CRC32_SIZE = 4,
COMPRESSED_POS = CRC32_POS + CRC32_SIZE,
COMPRESSED_SIZE = 8,
UNCOMPRESSED_POS = COMPRESSED_POS + COMPRESSED_SIZE,
UNCOMPRESSED_SIZE = 8,
FULLHEADER_SIZE = UNCOMPRESSED_POS + UNCOMPRESSED_SIZE
};
char _rawInfo[FULLHEADER_SIZE];
bool _valid;
};
inline const char* ZipDataInfo64::getRawHeader() const
{
return _rawInfo;
}
inline bool ZipDataInfo64::isValid() const
{
return _valid;
}
inline Poco::UInt32 ZipDataInfo64::getCRC32() const
{
return ZipUtil::get32BitValue(_rawInfo, CRC32_POS);
}
inline void ZipDataInfo64::setCRC32(Poco::UInt32 crc)
{
return ZipUtil::set32BitValue(crc, _rawInfo, CRC32_POS);
}
inline Poco::UInt64 ZipDataInfo64::getCompressedSize() const
{
return ZipUtil::get64BitValue(_rawInfo, COMPRESSED_POS);
}
inline void ZipDataInfo64::setCompressedSize(Poco::UInt64 size)
{
return ZipUtil::set64BitValue(size, _rawInfo, COMPRESSED_POS);
}
inline Poco::UInt64 ZipDataInfo64::getUncompressedSize() const
{
return ZipUtil::get64BitValue(_rawInfo, UNCOMPRESSED_POS);
}
inline void ZipDataInfo64::setUncompressedSize(Poco::UInt64 size)
{
return ZipUtil::set64BitValue(size, _rawInfo, UNCOMPRESSED_POS);
}
inline Poco::UInt32 ZipDataInfo64::getFullHeaderSize()
{
return FULLHEADER_SIZE;
}
} } // namespace Poco::Zip } } // namespace Poco::Zip
#endif // Zip_ZipDataInfo_INCLUDED #endif // Zip_ZipDataInfo_INCLUDED

View File

@ -48,9 +48,6 @@ public:
~ZipFileInfo(); ~ZipFileInfo();
/// Destroys the ZipFileInfo. /// Destroys the ZipFileInfo.
Poco::UInt32 getRelativeOffsetOfLocalHeader() const;
/// Where on the disk starts the localheader. Combined with the disk number gives the exact location of the header
ZipCommon::CompressionMethod getCompressionMethod() const; ZipCommon::CompressionMethod getCompressionMethod() const;
bool isEncrypted() const; bool isEncrypted() const;
@ -62,9 +59,12 @@ public:
Poco::UInt32 getHeaderSize() const; Poco::UInt32 getHeaderSize() const;
/// Returns the total size of the header including filename + other additional fields /// Returns the total size of the header including filename + other additional fields
Poco::UInt32 getCompressedSize() const; Poco::UInt64 getCompressedSize() const;
Poco::UInt32 getUncompressedSize() const; Poco::UInt64 getUncompressedSize() const;
Poco::UInt64 getOffset() const;
/// Where on the disk starts the localheader. Combined with the disk number gives the exact location of the header
const std::string& getFileName() const; const std::string& getFileName() const;
@ -94,14 +94,19 @@ public:
std::string createHeader() const; std::string createHeader() const;
void setOffset(Poco::UInt32 val); void setOffset(Poco::UInt64 val);
bool needsZip64() const;
void setZip64Data();
private: private:
void setCRC(Poco::UInt32 val); void setCRC(Poco::UInt32 val);
void setCompressedSize(Poco::UInt32 val); void setCompressedSize(Poco::UInt64 val);
void setUncompressedSize(Poco::UInt32 val); void setUncompressedSize(Poco::UInt64 val);
void setCompressionMethod(ZipCommon::CompressionMethod cm); void setCompressionMethod(ZipCommon::CompressionMethod cm);
@ -131,6 +136,8 @@ private:
Poco::UInt32 getUncompressedSizeFromHeader() const; Poco::UInt32 getUncompressedSizeFromHeader() const;
Poco::UInt32 getOffsetFromHeader() const;
Poco::UInt16 getFileNameLength() const; Poco::UInt16 getFileNameLength() const;
Poco::UInt16 getExtraFieldLength() const; Poco::UInt16 getExtraFieldLength() const;
@ -177,7 +184,17 @@ private:
EXTERNALFILE_ATTR_SIZE = 4, EXTERNALFILE_ATTR_SIZE = 4,
RELATIVEOFFSETLOCALHEADER_POS = EXTERNALFILE_ATTR_POS + EXTERNALFILE_ATTR_SIZE, RELATIVEOFFSETLOCALHEADER_POS = EXTERNALFILE_ATTR_POS + EXTERNALFILE_ATTR_SIZE,
RELATIVEOFFSETLOCALHEADER_SIZE = 4, RELATIVEOFFSETLOCALHEADER_SIZE = 4,
FULLHEADER_SIZE = 46 FULLHEADER_SIZE = 46,
EXTRA_DATA_TAG_SIZE = 2,
EXTRA_DATA_TAG_POS = 0,
EXTRA_DATA_SIZE_SIZE = 2,
EXTRA_DATA_SIZE_POS = EXTRA_DATA_TAG_POS + EXTRA_DATA_TAG_SIZE,
EXTRA_DATA_POS = EXTRA_DATA_SIZE_POS + EXTRA_DATA_SIZE_SIZE,
EXTRA_DATA_UNCOMPRESSED_SIZE_SIZE = 8,
EXTRA_DATA_COMPRESSED_SIZE_SIZE = 8,
EXTRA_DATA_OFFSET_SIZE = 8,
FULLEXTRA_DATA_SIZE = 28
}; };
enum enum
@ -188,8 +205,9 @@ private:
char _rawInfo[FULLHEADER_SIZE]; char _rawInfo[FULLHEADER_SIZE];
Poco::UInt32 _crc32; Poco::UInt32 _crc32;
Poco::UInt32 _compressedSize; Poco::UInt64 _compressedSize;
Poco::UInt32 _uncompressedSize; Poco::UInt64 _uncompressedSize;
Poco::UInt64 _localHeaderOffset;
std::string _fileName; std::string _fileName;
Poco::DateTime _lastModifiedAt; Poco::DateTime _lastModifiedAt;
std::string _extraField; std::string _extraField;
@ -197,11 +215,6 @@ private:
}; };
inline Poco::UInt32 ZipFileInfo::getRelativeOffsetOfLocalHeader() const
{
return ZipUtil::get32BitValue(_rawInfo, RELATIVEOFFSETLOCALHEADER_POS);
}
inline Poco::UInt32 ZipFileInfo::getCRCFromHeader() const inline Poco::UInt32 ZipFileInfo::getCRCFromHeader() const
{ {
@ -220,6 +233,11 @@ inline Poco::UInt32 ZipFileInfo::getUncompressedSizeFromHeader() const
return ZipUtil::get32BitValue(_rawInfo, UNCOMPRESSED_SIZE_POS); return ZipUtil::get32BitValue(_rawInfo, UNCOMPRESSED_SIZE_POS);
} }
inline Poco::UInt32 ZipFileInfo::getOffsetFromHeader() const
{
return ZipUtil::get32BitValue(_rawInfo, RELATIVEOFFSETLOCALHEADER_POS);
}
inline void ZipFileInfo::parseDateTime() inline void ZipFileInfo::parseDateTime()
{ {
@ -245,6 +263,10 @@ inline const Poco::DateTime& ZipFileInfo::lastModifiedAt() const
return _lastModifiedAt; return _lastModifiedAt;
} }
inline Poco::UInt64 ZipFileInfo::getOffset() const
{
return _localHeaderOffset;
}
inline Poco::UInt32 ZipFileInfo::getCRC() const inline Poco::UInt32 ZipFileInfo::getCRC() const
{ {
@ -252,13 +274,13 @@ inline Poco::UInt32 ZipFileInfo::getCRC() const
} }
inline Poco::UInt32 ZipFileInfo::getCompressedSize() const inline Poco::UInt64 ZipFileInfo::getCompressedSize() const
{ {
return _compressedSize; return _compressedSize;
} }
inline Poco::UInt32 ZipFileInfo::getUncompressedSize() const inline Poco::UInt64 ZipFileInfo::getUncompressedSize() const
{ {
return _uncompressedSize; return _uncompressedSize;
} }
@ -362,6 +384,30 @@ inline Poco::UInt32 ZipFileInfo::getHeaderSize() const
return FULLHEADER_SIZE + getFileNameLength() + getExtraFieldLength() + getFileCommentLength(); return FULLHEADER_SIZE + getFileNameLength() + getExtraFieldLength() + getFileCommentLength();
} }
inline bool ZipFileInfo::needsZip64() const {
return _localHeaderOffset >= ZipCommon::ZIP64_MAGIC || _compressedSize >= ZipCommon::ZIP64_MAGIC || _uncompressedSize >= ZipCommon::ZIP64_MAGIC;
}
inline void ZipFileInfo::setZip64Data() {
if(needsZip64()) {
setRequiredVersion(4, 5);
char data[FULLEXTRA_DATA_SIZE];
ZipUtil::set16BitValue(ZipCommon::ZIP64_EXTRA_ID, data, EXTRA_DATA_TAG_POS);
Poco::UInt16 pos = EXTRA_DATA_POS;
if(_uncompressedSize >= ZipCommon::ZIP64_MAGIC) {
ZipUtil::set64BitValue(_uncompressedSize, data, pos); pos += 8;
}
if(_compressedSize >= ZipCommon::ZIP64_MAGIC) {
ZipUtil::set64BitValue(_compressedSize, data, pos); pos += 8;
}
if(_localHeaderOffset >= ZipCommon::ZIP64_MAGIC) {
ZipUtil::set64BitValue(_localHeaderOffset, data, pos); pos += 8;
}
ZipUtil::set16BitValue(pos - EXTRA_DATA_POS, data, EXTRA_DATA_SIZE_POS);
_extraField = std::string(data, pos);
ZipUtil::set16BitValue(pos, _rawInfo, EXTRAFIELD_LENGTH_POS);
}
}
inline void ZipFileInfo::setCRC(Poco::UInt32 val) inline void ZipFileInfo::setCRC(Poco::UInt32 val)
{ {
@ -370,23 +416,24 @@ inline void ZipFileInfo::setCRC(Poco::UInt32 val)
} }
inline void ZipFileInfo::setOffset(Poco::UInt32 val) inline void ZipFileInfo::setOffset(Poco::UInt64 val)
{ {
ZipUtil::set32BitValue(val, _rawInfo, RELATIVEOFFSETLOCALHEADER_POS); _localHeaderOffset = val;
ZipUtil::set32BitValue(val >= ZipCommon::ZIP64_MAGIC ? ZipCommon::ZIP64_MAGIC : static_cast<Poco::UInt32>(val), _rawInfo, RELATIVEOFFSETLOCALHEADER_POS);
} }
inline void ZipFileInfo::setCompressedSize(Poco::UInt32 val) inline void ZipFileInfo::setCompressedSize(Poco::UInt64 val)
{ {
_compressedSize = val; _compressedSize = val;
ZipUtil::set32BitValue(val, _rawInfo, COMPRESSED_SIZE_POS); ZipUtil::set32BitValue(val >= ZipCommon::ZIP64_MAGIC ? ZipCommon::ZIP64_MAGIC : static_cast<Poco::UInt32>(val), _rawInfo, COMPRESSED_SIZE_POS);
} }
inline void ZipFileInfo::setUncompressedSize(Poco::UInt32 val) inline void ZipFileInfo::setUncompressedSize(Poco::UInt64 val)
{ {
_uncompressedSize = val; _uncompressedSize = val;
ZipUtil::set32BitValue(val, _rawInfo, UNCOMPRESSED_SIZE_POS); ZipUtil::set32BitValue(val >= ZipCommon::ZIP64_MAGIC ? ZipCommon::ZIP64_MAGIC : static_cast<Poco::UInt32>(val), _rawInfo, UNCOMPRESSED_SIZE_POS);
} }

View File

@ -41,9 +41,10 @@ class Zip_API ZipLocalFileHeader
public: public:
static const char HEADER[ZipCommon::HEADER_SIZE]; static const char HEADER[ZipCommon::HEADER_SIZE];
ZipLocalFileHeader(const Poco::Path& fileName, const Poco::DateTime& lastModifiedAt, ZipCommon::CompressionMethod cm, ZipCommon::CompressionLevel cl); ZipLocalFileHeader(const Poco::Path& fileName, const Poco::DateTime& lastModifiedAt, ZipCommon::CompressionMethod cm, ZipCommon::CompressionLevel cl, bool forceZip64 = false);
/// Creates a zip file header from an absoluteFile. fileName is the name of the file in the zip, outputIsSeekable determines if we write /// Creates a zip file header from an absoluteFile. fileName is the name of the file in the zip, outputIsSeekable determines if we write
/// CRC and file sizes to the LocalFileHeader or after data compression into a ZipDataInfo /// CRC and file sizes to the LocalFileHeader or after data compression into a ZipDataInfo
/// If forceZip64 is set true then the file header is allocated with zip64 extension.
ZipLocalFileHeader(std::istream& inp, bool assumeHeaderRead, ParseCallback& callback); ZipLocalFileHeader(std::istream& inp, bool assumeHeaderRead, ParseCallback& callback);
/// Creates the ZipLocalFileHeader by parsing the input stream. /// Creates the ZipLocalFileHeader by parsing the input stream.
@ -90,15 +91,15 @@ public:
Poco::UInt32 getCRC() const; Poco::UInt32 getCRC() const;
Poco::UInt32 getCompressedSize() const; Poco::UInt64 getCompressedSize() const;
Poco::UInt32 getUncompressedSize() const; Poco::UInt64 getUncompressedSize() const;
void setCRC(Poco::UInt32 val); void setCRC(Poco::UInt32 val);
void setCompressedSize(Poco::UInt32 val); void setCompressedSize(Poco::UInt64 val);
void setUncompressedSize(Poco::UInt32 val); void setUncompressedSize(Poco::UInt64 val);
const std::string& getFileName() const; const std::string& getFileName() const;
@ -118,6 +119,10 @@ public:
void setFileName(const std::string& fileName, bool isDirectory); void setFileName(const std::string& fileName, bool isDirectory);
bool needsZip64() const;
void setZip64Data();
std::string createHeader() const; std::string createHeader() const;
/// Creates a header /// Creates a header
@ -170,17 +175,27 @@ private:
LASTMODEFILEDATE_POS = LASTMODEFILETIME_POS + LASTMODEFILETIME_SIZE, LASTMODEFILEDATE_POS = LASTMODEFILETIME_POS + LASTMODEFILETIME_SIZE,
CRC32_SIZE = 4, CRC32_SIZE = 4,
CRC32_POS = LASTMODEFILEDATE_POS + LASTMODEFILEDATE_SIZE, CRC32_POS = LASTMODEFILEDATE_POS + LASTMODEFILEDATE_SIZE,
COMPRESSEDSIZE_SIZE = 4, COMPRESSED_SIZE_SIZE = 4,
COMPRESSEDSIZE_POS = CRC32_POS + CRC32_SIZE, COMPRESSED_SIZE_POS = CRC32_POS + CRC32_SIZE,
UNCOMPRESSEDSIZE_SIZE = 4, UNCOMPRESSED_SIZE_SIZE = 4,
UNCOMPRESSEDSIZE_POS = COMPRESSEDSIZE_POS + COMPRESSEDSIZE_SIZE, UNCOMPRESSED_SIZE_POS = COMPRESSED_SIZE_POS + COMPRESSED_SIZE_SIZE,
FILELENGTH_SIZE = 2, FILE_LENGTH_SIZE = 2,
FILELENGTH_POS = UNCOMPRESSEDSIZE_POS + UNCOMPRESSEDSIZE_SIZE, FILE_LENGTH_POS = UNCOMPRESSED_SIZE_POS + UNCOMPRESSED_SIZE_SIZE,
EXTRAFIELD_LENGTH = 2, EXTRA_FIELD_LENGTH = 2,
EXTRAFIELD_POS = FILELENGTH_POS + FILELENGTH_SIZE, EXTRA_FIELD_POS = FILE_LENGTH_POS + FILE_LENGTH_SIZE,
FULLHEADER_SIZE = 30 FULLHEADER_SIZE = 30,
EXTRA_DATA_TAG_SIZE = 2,
EXTRA_DATA_TAG_POS = 0,
EXTRA_DATA_SIZE_SIZE = 2,
EXTRA_DATA_SIZE_POS = EXTRA_DATA_TAG_POS + EXTRA_DATA_TAG_SIZE,
EXTRA_DATA_POS = EXTRA_DATA_SIZE_POS + EXTRA_DATA_SIZE_SIZE,
EXTRA_DATA_UNCOMPRESSED_SIZE_SIZE = 8,
EXTRA_DATA_COMPRESSED_SIZE_SIZE = 8,
FULLEXTRA_DATA_SIZE = 20
}; };
bool _forceZip64;
char _rawHeader[FULLHEADER_SIZE]; char _rawHeader[FULLHEADER_SIZE];
std::streamoff _startPos; std::streamoff _startPos;
std::streamoff _endPos; std::streamoff _endPos;
@ -188,20 +203,20 @@ private:
Poco::DateTime _lastModifiedAt; Poco::DateTime _lastModifiedAt;
std::string _extraField; std::string _extraField;
Poco::UInt32 _crc32; Poco::UInt32 _crc32;
Poco::UInt32 _compressedSize; Poco::UInt64 _compressedSize;
Poco::UInt32 _uncompressedSize; Poco::UInt64 _uncompressedSize;
}; };
inline void ZipLocalFileHeader::setFileNameLength(Poco::UInt16 size) inline void ZipLocalFileHeader::setFileNameLength(Poco::UInt16 size)
{ {
ZipUtil::set16BitValue(size, _rawHeader, FILELENGTH_POS); ZipUtil::set16BitValue(size, _rawHeader, FILE_LENGTH_POS);
} }
inline void ZipLocalFileHeader::setExtraFieldSize(Poco::UInt16 size) inline void ZipLocalFileHeader::setExtraFieldSize(Poco::UInt16 size)
{ {
ZipUtil::set16BitValue(size, _rawHeader, EXTRAFIELD_POS); ZipUtil::set16BitValue(size, _rawHeader, EXTRA_FIELD_POS);
} }
@ -235,6 +250,23 @@ inline void ZipLocalFileHeader::getRequiredVersion(int& major, int& minor)
minor = getMinorVersionNumber(); minor = getMinorVersionNumber();
} }
inline bool ZipLocalFileHeader::needsZip64() const {
return _forceZip64 || _startPos >= ZipCommon::ZIP64_MAGIC || _compressedSize >= ZipCommon::ZIP64_MAGIC || _uncompressedSize >= ZipCommon::ZIP64_MAGIC;
}
inline void ZipLocalFileHeader::setZip64Data() {
setRequiredVersion(4, 5);
char data[FULLEXTRA_DATA_SIZE];
ZipUtil::set16BitValue(ZipCommon::ZIP64_EXTRA_ID, data, EXTRA_DATA_TAG_POS);
Poco::UInt16 pos = EXTRA_DATA_POS;
ZipUtil::set64BitValue(_uncompressedSize, data, pos); pos += 8;
ZipUtil::set32BitValue(ZipCommon::ZIP64_MAGIC, _rawHeader, UNCOMPRESSED_SIZE_POS);
ZipUtil::set64BitValue(_compressedSize, data, pos); pos += 8;
ZipUtil::set32BitValue(ZipCommon::ZIP64_MAGIC, _rawHeader, COMPRESSED_SIZE_POS);
ZipUtil::set16BitValue(pos - EXTRA_DATA_POS, data, EXTRA_DATA_SIZE_POS);
_extraField = std::string(data, pos);
ZipUtil::set16BitValue(pos, _rawHeader, EXTRA_FIELD_POS);
}
inline void ZipLocalFileHeader::setRequiredVersion(int major, int minor) inline void ZipLocalFileHeader::setRequiredVersion(int major, int minor)
{ {
@ -245,13 +277,13 @@ inline void ZipLocalFileHeader::setRequiredVersion(int major, int minor)
inline Poco::UInt16 ZipLocalFileHeader::getFileNameLength() const inline Poco::UInt16 ZipLocalFileHeader::getFileNameLength() const
{ {
return ZipUtil::get16BitValue(_rawHeader, FILELENGTH_POS); return ZipUtil::get16BitValue(_rawHeader, FILE_LENGTH_POS);
} }
inline Poco::UInt16 ZipLocalFileHeader::getExtraFieldLength() const inline Poco::UInt16 ZipLocalFileHeader::getExtraFieldLength() const
{ {
return ZipUtil::get16BitValue(_rawHeader, EXTRAFIELD_POS); return ZipUtil::get16BitValue(_rawHeader, EXTRA_FIELD_POS);
} }
@ -360,13 +392,13 @@ inline Poco::UInt32 ZipLocalFileHeader::getCRC() const
} }
inline Poco::UInt32 ZipLocalFileHeader::getCompressedSize() const inline Poco::UInt64 ZipLocalFileHeader::getCompressedSize() const
{ {
return _compressedSize; return _compressedSize;
} }
inline Poco::UInt32 ZipLocalFileHeader::getUncompressedSize() const inline Poco::UInt64 ZipLocalFileHeader::getUncompressedSize() const
{ {
return _uncompressedSize; return _uncompressedSize;
} }
@ -379,17 +411,17 @@ inline void ZipLocalFileHeader::setCRC(Poco::UInt32 val)
} }
inline void ZipLocalFileHeader::setCompressedSize(Poco::UInt32 val) inline void ZipLocalFileHeader::setCompressedSize(Poco::UInt64 val)
{ {
_compressedSize = val; _compressedSize = val;
ZipUtil::set32BitValue(val, _rawHeader, COMPRESSEDSIZE_POS); ZipUtil::set32BitValue(val >= ZipCommon::ZIP64_MAGIC ? ZipCommon::ZIP64_MAGIC : static_cast<Poco::UInt32>(val), _rawHeader, COMPRESSED_SIZE_POS);
} }
inline void ZipLocalFileHeader::setUncompressedSize(Poco::UInt32 val) inline void ZipLocalFileHeader::setUncompressedSize(Poco::UInt64 val)
{ {
_uncompressedSize = val; _uncompressedSize = val;
ZipUtil::set32BitValue(val, _rawHeader, UNCOMPRESSEDSIZE_POS); ZipUtil::set32BitValue(val >= ZipCommon::ZIP64_MAGIC ? ZipCommon::ZIP64_MAGIC : static_cast<Poco::UInt32>(val), _rawHeader, UNCOMPRESSED_SIZE_POS);
} }
@ -401,13 +433,13 @@ inline Poco::UInt32 ZipLocalFileHeader::getCRCFromHeader() const
inline Poco::UInt32 ZipLocalFileHeader::getCompressedSizeFromHeader() const inline Poco::UInt32 ZipLocalFileHeader::getCompressedSizeFromHeader() const
{ {
return ZipUtil::get32BitValue(_rawHeader, COMPRESSEDSIZE_POS); return ZipUtil::get32BitValue(_rawHeader, COMPRESSED_SIZE_POS);
} }
inline Poco::UInt32 ZipLocalFileHeader::getUncompressedSizeFromHeader() const inline Poco::UInt32 ZipLocalFileHeader::getUncompressedSizeFromHeader() const
{ {
return ZipUtil::get32BitValue(_rawHeader, UNCOMPRESSEDSIZE_POS); return ZipUtil::get32BitValue(_rawHeader, UNCOMPRESSED_SIZE_POS);
} }

View File

@ -50,7 +50,7 @@ public:
virtual ~ZipStreamBuf(); virtual ~ZipStreamBuf();
/// Destroys the ZipStreamBuf. /// Destroys the ZipStreamBuf.
void close(); void close(Poco::UInt64& extraDataSize);
/// Informs a writing outputstream that writing is done for this stream /// Informs a writing outputstream that writing is done for this stream
bool crcValid() const; bool crcValid() const;
@ -79,7 +79,7 @@ private:
Poco::UInt32 _expectedCrc32; Poco::UInt32 _expectedCrc32;
bool _checkCRC; bool _checkCRC;
/// Note: we do not check crc if we decompress a streaming zip file and the crc is stored in the directory header /// Note: we do not check crc if we decompress a streaming zip file and the crc is stored in the directory header
Poco::UInt32 _bytesWritten; Poco::UInt64 _bytesWritten;
ZipLocalFileHeader* _pHeader; ZipLocalFileHeader* _pHeader;
}; };
@ -140,7 +140,7 @@ public:
~ZipOutputStream(); ~ZipOutputStream();
/// Destroys the ZipOutputStream. /// Destroys the ZipOutputStream.
void close(); void close(Poco::UInt64& extraDataSize);
/// Must be called for ZipOutputStreams! /// Must be called for ZipOutputStreams!
}; };

View File

@ -39,10 +39,14 @@ public:
static Poco::UInt32 get32BitValue(const char* pVal, const Poco::UInt32 pos); static Poco::UInt32 get32BitValue(const char* pVal, const Poco::UInt32 pos);
static void set16BitValue(const Poco::UInt16 val, char* pVal, const Poco::UInt32 pos); static Poco::UInt64 get64BitValue(const char* pVal, const Poco::UInt32 pos);
static void set16BitValue(const Poco::UInt16 val, char* pVal, const Poco::UInt32 pos);
static void set32BitValue(const Poco::UInt32 val, char* pVal, const Poco::UInt32 pos); static void set32BitValue(const Poco::UInt32 val, char* pVal, const Poco::UInt32 pos);
static void set64BitValue(const Poco::UInt64 val, char* pVal, const Poco::UInt32 pos);
static Poco::DateTime parseDateTime(const char* pVal, const Poco::UInt32 timePos, const Poco::UInt32 datePos); static Poco::DateTime parseDateTime(const char* pVal, const Poco::UInt32 timePos, const Poco::UInt32 datePos);
static void setDateTime(const Poco::DateTime& dt, char* pVal, const Poco::UInt32 timePos, const Poco::UInt32 datePos); static void setDateTime(const Poco::DateTime& dt, char* pVal, const Poco::UInt32 timePos, const Poco::UInt32 datePos);
@ -77,6 +81,12 @@ inline Poco::UInt32 ZipUtil::get32BitValue(const char* pVal, const Poco::UInt32
(static_cast<Poco::UInt32>((unsigned char)pVal[pos+2]) << 16) + (static_cast<Poco::UInt32>((unsigned char)pVal[pos+3]) << 24); (static_cast<Poco::UInt32>((unsigned char)pVal[pos+2]) << 16) + (static_cast<Poco::UInt32>((unsigned char)pVal[pos+3]) << 24);
} }
inline Poco::UInt64 ZipUtil::get64BitValue(const char* pVal, const Poco::UInt32 pos)
{
Poco::UInt64 val = ZipUtil::get32BitValue(pVal, pos+4);
val = (val << 32) | ZipUtil::get32BitValue(pVal, pos);
return val;
}
inline void ZipUtil::set16BitValue(const Poco::UInt16 val, char* pVal, const Poco::UInt32 pos) inline void ZipUtil::set16BitValue(const Poco::UInt16 val, char* pVal, const Poco::UInt32 pos)
{ {
@ -93,6 +103,17 @@ inline void ZipUtil::set32BitValue(const Poco::UInt32 val, char* pVal, const Poc
pVal[pos+3] = static_cast<char>(val>>24); pVal[pos+3] = static_cast<char>(val>>24);
} }
inline void ZipUtil::set64BitValue(const Poco::UInt64 val, char* pVal, const Poco::UInt32 pos)
{
pVal[pos] = static_cast<char>(val);
pVal[pos+1] = static_cast<char>(val>>8);
pVal[pos+2] = static_cast<char>(val>>16);
pVal[pos+3] = static_cast<char>(val>>24);
pVal[pos+4] = static_cast<char>(val>>32);
pVal[pos+5] = static_cast<char>(val>>40);
pVal[pos+6] = static_cast<char>(val>>48);
pVal[pos+7] = static_cast<char>(val>>56);
}
} } // namespace Poco::Zip } } // namespace Poco::Zip

View File

@ -30,9 +30,10 @@ namespace Poco {
namespace Zip { namespace Zip {
Compress::Compress(std::ostream& out, bool seekableOut): Compress::Compress(std::ostream& out, bool seekableOut, bool forceZip64):
_out(out), _out(out),
_seekableOut(seekableOut), _seekableOut(seekableOut),
_forceZip64(forceZip64),
_files(), _files(),
_infos(), _infos(),
_dirs(), _dirs(),
@ -63,26 +64,24 @@ void Compress::addEntry(std::istream& in, const Poco::DateTime& lastModifiedAt,
std::string fn = ZipUtil::validZipEntryFileName(fileName); std::string fn = ZipUtil::validZipEntryFileName(fileName);
if (_files.size() >= 65535)
throw ZipException("Maximum number of entries for a ZIP file reached: 65535");
if (!in.good()) if (!in.good())
throw ZipException("Invalid input stream"); throw ZipException("Invalid input stream");
std::streamoff localHeaderOffset = _offset; std::streamoff localHeaderOffset = _offset;
ZipLocalFileHeader hdr(fileName, lastModifiedAt, cm, cl); ZipLocalFileHeader hdr(fileName, lastModifiedAt, cm, cl, _forceZip64);
hdr.setStartPos(localHeaderOffset); hdr.setStartPos(localHeaderOffset);
ZipOutputStream zipOut(_out, hdr, _seekableOut); ZipOutputStream zipOut(_out, hdr, _seekableOut);
Poco::StreamCopier::copyStream(in, zipOut); Poco::StreamCopier::copyStream(in, zipOut);
zipOut.close(); Poco::UInt64 extraDataSize;
hdr.setStartPos(localHeaderOffset); // reset again now that compressed Size is known zipOut.close(extraDataSize);
_offset = hdr.getEndPos(); _offset = hdr.getEndPos();
if (hdr.searchCRCAndSizesAfterData()) _offset += extraDataSize;
_offset += ZipDataInfo::getFullHeaderSize();
_files.insert(std::make_pair(fileName.toString(Poco::Path::PATH_UNIX), hdr)); _files.insert(std::make_pair(fileName.toString(Poco::Path::PATH_UNIX), hdr));
poco_assert (_out); poco_assert (_out);
ZipFileInfo nfo(hdr); ZipFileInfo nfo(hdr);
nfo.setOffset(localHeaderOffset); nfo.setOffset(localHeaderOffset);
nfo.setZip64Data();
_infos.insert(std::make_pair(fileName.toString(Poco::Path::PATH_UNIX), nfo)); _infos.insert(std::make_pair(fileName.toString(Poco::Path::PATH_UNIX), nfo));
EDone.notify(this, hdr); EDone.notify(this, hdr);
} }
@ -94,8 +93,6 @@ void Compress::addFileRaw(std::istream& in, const ZipLocalFileHeader& h, const P
//bypass the header of the input stream and point to the first byte of the data payload //bypass the header of the input stream and point to the first byte of the data payload
in.seekg(h.getDataStartPos(), std::ios_base::beg); in.seekg(h.getDataStartPos(), std::ios_base::beg);
if (_files.size() >= 65535)
throw ZipException("Maximum number of entries for a ZIP file reached: 65535");
if (!in.good()) if (!in.good())
throw ZipException("Invalid input stream"); throw ZipException("Invalid input stream");
@ -103,16 +100,18 @@ void Compress::addFileRaw(std::istream& in, const ZipLocalFileHeader& h, const P
ZipLocalFileHeader hdr(h); ZipLocalFileHeader hdr(h);
hdr.setFileName(fn, h.isDirectory()); hdr.setFileName(fn, h.isDirectory());
hdr.setStartPos(localHeaderOffset); hdr.setStartPos(localHeaderOffset);
if(hdr.needsZip64())
hdr.setZip64Data();
//bypass zipoutputstream //bypass zipoutputstream
//write the header directly //write the header directly
std::string header = hdr.createHeader(); std::string header = hdr.createHeader();
_out.write(header.c_str(), static_cast<std::streamsize>(header.size())); _out.write(header.c_str(), static_cast<std::streamsize>(header.size()));
// now fwd the payload to _out in chunks of size CHUNKSIZE // now fwd the payload to _out in chunks of size CHUNKSIZE
Poco::UInt32 totalSize = hdr.getCompressedSize(); Poco::UInt64 totalSize = hdr.getCompressedSize();
if (totalSize > 0) if (totalSize > 0)
{ {
Poco::Buffer<char> buffer(COMPRESS_CHUNK_SIZE); Poco::Buffer<char> buffer(COMPRESS_CHUNK_SIZE);
Poco::UInt32 remaining = totalSize; Poco::UInt64 remaining = totalSize;
while(remaining > 0) while(remaining > 0)
{ {
if (remaining > COMPRESS_CHUNK_SIZE) if (remaining > COMPRESS_CHUNK_SIZE)
@ -133,20 +132,33 @@ void Compress::addFileRaw(std::istream& in, const ZipLocalFileHeader& h, const P
} }
} }
} }
//write optional block afterwards hdr.setStartPos(localHeaderOffset); // This resets EndPos now that compressed Size is known
if (hdr.searchCRCAndSizesAfterData())
{
ZipDataInfo info(in, false);
_out.write(info.getRawHeader(), static_cast<std::streamsize>(info.getFullHeaderSize()));
}
hdr.setStartPos(localHeaderOffset); // reset again now that compressed Size is known
_offset = hdr.getEndPos(); _offset = hdr.getEndPos();
if (hdr.searchCRCAndSizesAfterData()) //write optional block afterwards
_offset += ZipDataInfo::getFullHeaderSize(); if (hdr.searchCRCAndSizesAfterData()) {
if(hdr.needsZip64()) {
ZipDataInfo64 info(in, false);
_out.write(info.getRawHeader(), static_cast<std::streamsize>(info.getFullHeaderSize()));
_offset += ZipDataInfo::getFullHeaderSize();
} else {
ZipDataInfo info(in, false);
_out.write(info.getRawHeader(), static_cast<std::streamsize>(info.getFullHeaderSize()));
_offset += ZipDataInfo::getFullHeaderSize();
}
} else {
if(hdr.hasExtraField()) // Update sizes in header extension.
hdr.setZip64Data();
_out.seekp(hdr.getStartPos(), std::ios_base::beg);
std::string header = hdr.createHeader();
_out.write(header.c_str(), static_cast<std::streamsize>(header.size()));
_out.seekp(0, std::ios_base::end);
}
_files.insert(std::make_pair(fileName.toString(Poco::Path::PATH_UNIX), hdr)); _files.insert(std::make_pair(fileName.toString(Poco::Path::PATH_UNIX), hdr));
poco_assert (_out); poco_assert (_out);
ZipFileInfo nfo(hdr); ZipFileInfo nfo(hdr);
nfo.setOffset(localHeaderOffset); nfo.setOffset(localHeaderOffset);
nfo.setZip64Data();
_infos.insert(std::make_pair(fileName.toString(Poco::Path::PATH_UNIX), nfo)); _infos.insert(std::make_pair(fileName.toString(Poco::Path::PATH_UNIX), nfo));
EDone.notify(this, hdr); EDone.notify(this, hdr);
} }
@ -186,8 +198,6 @@ void Compress::addDirectory(const Poco::Path& entryName, const Poco::DateTime& l
std::string fileStr = entryName.toString(Poco::Path::PATH_UNIX); std::string fileStr = entryName.toString(Poco::Path::PATH_UNIX);
if (_files.find(fileStr) != _files.end()) if (_files.find(fileStr) != _files.end())
return; // ignore duplicate add return; // ignore duplicate add
if (_files.size() >= 65535)
throw ZipException("Maximum number of entries for a ZIP file reached: 65535");
if (fileStr == "/") if (fileStr == "/")
throw ZipException("Illegal entry name /"); throw ZipException("Illegal entry name /");
if (fileStr.empty()) if (fileStr.empty())
@ -206,15 +216,17 @@ void Compress::addDirectory(const Poco::Path& entryName, const Poco::DateTime& l
ZipLocalFileHeader hdr(entryName, lastModifiedAt, cm, cl); ZipLocalFileHeader hdr(entryName, lastModifiedAt, cm, cl);
hdr.setStartPos(localHeaderOffset); hdr.setStartPos(localHeaderOffset);
ZipOutputStream zipOut(_out, hdr, _seekableOut); ZipOutputStream zipOut(_out, hdr, _seekableOut);
zipOut.close(); Poco::UInt64 extraDataSize;
zipOut.close(extraDataSize);
hdr.setStartPos(localHeaderOffset); // reset again now that compressed Size is known hdr.setStartPos(localHeaderOffset); // reset again now that compressed Size is known
_offset = hdr.getEndPos(); _offset = hdr.getEndPos();
if (hdr.searchCRCAndSizesAfterData()) if (hdr.searchCRCAndSizesAfterData())
_offset += ZipDataInfo::getFullHeaderSize(); _offset += extraDataSize;
_files.insert(std::make_pair(entryName.toString(Poco::Path::PATH_UNIX), hdr)); _files.insert(std::make_pair(entryName.toString(Poco::Path::PATH_UNIX), hdr));
poco_assert (_out); poco_assert (_out);
ZipFileInfo nfo(hdr); ZipFileInfo nfo(hdr);
nfo.setOffset(localHeaderOffset); nfo.setOffset(localHeaderOffset);
nfo.setZip64Data();
_infos.insert(std::make_pair(entryName.toString(Poco::Path::PATH_UNIX), nfo)); _infos.insert(std::make_pair(entryName.toString(Poco::Path::PATH_UNIX), nfo));
EDone.notify(this, hdr); EDone.notify(this, hdr);
} }
@ -274,34 +286,57 @@ void Compress::addRecursive(const Poco::Path& entry, ZipCommon::CompressionMetho
ZipArchive Compress::close() ZipArchive Compress::close()
{ {
if (!_dirs.empty()) if (!_dirs.empty() || ! _dirs64.empty())
return ZipArchive(_files, _infos, _dirs); return ZipArchive(_files, _infos, _dirs, _dirs64);
poco_assert (_infos.size() == _files.size()); poco_assert (_infos.size() == _files.size());
poco_assert (_files.size() < 65536); Poco::UInt64 centralDirSize64 = 0;
Poco::UInt32 centralDirStart = _offset; Poco::UInt64 centralDirStart64 = _offset;
Poco::UInt32 centralDirSize = 0;
// write all infos // write all infos
ZipArchive::FileInfos::const_iterator it = _infos.begin(); ZipArchive::FileInfos::const_iterator it = _infos.begin();
ZipArchive::FileInfos::const_iterator itEnd = _infos.end(); ZipArchive::FileInfos::const_iterator itEnd = _infos.end();
bool needZip64 = _forceZip64;
needZip64 = needZip64 || _files.size() >= ZipCommon::ZIP64_MAGIC_SHORT || centralDirStart64 >= ZipCommon::ZIP64_MAGIC;
for (; it != itEnd; ++it) for (; it != itEnd; ++it)
{ {
const ZipFileInfo& nfo = it->second; const ZipFileInfo& nfo = it->second;
needZip64 = needZip64 || nfo.needsZip64();
std::string info(nfo.createHeader()); std::string info(nfo.createHeader());
_out.write(info.c_str(), static_cast<std::streamsize>(info.size())); _out.write(info.c_str(), static_cast<std::streamsize>(info.size()));
Poco::UInt32 entrySize = static_cast<Poco::UInt32>(info.size()); Poco::UInt32 entrySize = static_cast<Poco::UInt32>(info.size());
centralDirSize += entrySize; centralDirSize64 += entrySize;
_offset += entrySize; _offset += entrySize;
} }
poco_assert (_out); poco_assert (_out);
Poco::UInt16 numEntries = static_cast<Poco::UInt16>(_infos.size()); Poco::UInt64 numEntries64 = _infos.size();
needZip64 = needZip64 || _offset >= ZipCommon::ZIP64_MAGIC;
if(needZip64) {
ZipArchiveInfo64 central;
central.setCentralDirectorySize(centralDirSize64);
central.setCentralDirectoryOffset(centralDirStart64);
central.setNumberOfEntries(numEntries64);
central.setTotalNumberOfEntries(numEntries64);
central.setHeaderOffset(_offset);
central.setTotalNumberOfDisks(1);
std::string centr(central.createHeader());
_out.write(centr.c_str(), static_cast<std::streamsize>(centr.size()));
_out.flush();
_offset += centr.size();
_dirs64.insert(std::make_pair(0, central));
}
Poco::UInt16 numEntries = numEntries64 >= ZipCommon::ZIP64_MAGIC_SHORT ? ZipCommon::ZIP64_MAGIC_SHORT : static_cast<Poco::UInt16>(numEntries64);
Poco::UInt32 centralDirStart = centralDirStart64 >= ZipCommon::ZIP64_MAGIC ? ZipCommon::ZIP64_MAGIC : static_cast<Poco::UInt32>(centralDirStart64);
Poco::UInt32 centralDirSize = centralDirSize64 >= ZipCommon::ZIP64_MAGIC ? ZipCommon::ZIP64_MAGIC : static_cast<Poco::UInt32>(centralDirSize64);
Poco::UInt32 offset = _offset >= ZipCommon::ZIP64_MAGIC ? ZipCommon::ZIP64_MAGIC : static_cast<Poco::UInt32>(_offset);
ZipArchiveInfo central; ZipArchiveInfo central;
central.setCentralDirectorySize(centralDirSize); central.setCentralDirectorySize(centralDirSize);
central.setCentralDirectoryOffset(centralDirStart);
central.setNumberOfEntries(numEntries); central.setNumberOfEntries(numEntries);
central.setTotalNumberOfEntries(numEntries); central.setTotalNumberOfEntries(numEntries);
central.setHeaderOffset(centralDirStart); central.setHeaderOffset(offset);
if (!_comment.empty() && _comment.size() <= 65535) if (!_comment.empty() && _comment.size() <= 65535)
{ {
central.setZipComment(_comment); central.setZipComment(_comment);
@ -309,11 +344,11 @@ ZipArchive Compress::close()
std::string centr(central.createHeader()); std::string centr(central.createHeader());
_out.write(centr.c_str(), static_cast<std::streamsize>(centr.size())); _out.write(centr.c_str(), static_cast<std::streamsize>(centr.size()));
_out.flush(); _out.flush();
_offset += centr.size();
_dirs.insert(std::make_pair(0, central)); _dirs.insert(std::make_pair(0, central));
return ZipArchive(_files, _infos, _dirs); return ZipArchive(_files, _infos, _dirs, _dirs64);
} }
void Compress::setStoreExtensions(const std::set<std::string>& extensions) void Compress::setStoreExtensions(const std::set<std::string>& extensions)
{ {
_storeExtensions.clear(); _storeExtensions.clear();

View File

@ -27,7 +27,8 @@ namespace Zip {
ZipArchive::ZipArchive(std::istream& in): ZipArchive::ZipArchive(std::istream& in):
_entries(), _entries(),
_infos(), _infos(),
_disks() _disks(),
_disks64()
{ {
poco_assert_dbg (in); poco_assert_dbg (in);
SkipCallback skip; SkipCallback skip;
@ -35,10 +36,11 @@ ZipArchive::ZipArchive(std::istream& in):
} }
ZipArchive::ZipArchive(const FileHeaders& entries, const FileInfos& infos, const DirectoryInfos& dirs): ZipArchive::ZipArchive(const FileHeaders& entries, const FileInfos& infos, const DirectoryInfos& dirs, const DirectoryInfos64& dirs64):
_entries(entries), _entries(entries),
_infos(infos), _infos(infos),
_disks(dirs) _disks(dirs),
_disks64(dirs64)
{ {
} }
@ -46,7 +48,8 @@ ZipArchive::ZipArchive(const FileHeaders& entries, const FileInfos& infos, const
ZipArchive::ZipArchive(std::istream& in, ParseCallback& pc): ZipArchive::ZipArchive(std::istream& in, ParseCallback& pc):
_entries(), _entries(),
_infos(), _infos(),
_disks() _disks(),
_disks64()
{ {
poco_assert_dbg (in); poco_assert_dbg (in);
parse(in, pc); parse(in, pc);
@ -78,7 +81,7 @@ void ZipArchive::parse(std::istream& in, ParseCallback& pc)
FileHeaders::iterator it = _entries.find(info.getFileName()); FileHeaders::iterator it = _entries.find(info.getFileName());
if (it != _entries.end()) if (it != _entries.end())
{ {
it->second.setStartPos(info.getRelativeOffsetOfLocalHeader()); it->second.setStartPos(info.getOffset());
} }
poco_assert (_infos.insert(std::make_pair(info.getFileName(), info)).second); poco_assert (_infos.insert(std::make_pair(info.getFileName(), info)).second);
} }
@ -87,9 +90,14 @@ void ZipArchive::parse(std::istream& in, ParseCallback& pc)
ZipArchiveInfo nfo(in, true); ZipArchiveInfo nfo(in, true);
poco_assert (_disks.insert(std::make_pair(nfo.getDiskNumber(), nfo)).second); poco_assert (_disks.insert(std::make_pair(nfo.getDiskNumber(), nfo)).second);
} }
else if (std::memcmp(header, ZipArchiveInfo64::HEADER, ZipCommon::HEADER_SIZE) == 0)
{
ZipArchiveInfo64 nfo(in, true);
poco_assert (_disks64.insert(std::make_pair(nfo.getDiskNumber(), nfo)).second);
}
else else
{ {
if (_disks.empty()) if (_disks.empty() && _disks64.empty())
throw Poco::IllegalStateException("Illegal header in zip file"); throw Poco::IllegalStateException("Illegal header in zip file");
else else
throw Poco::IllegalStateException("Garbage after directory header"); throw Poco::IllegalStateException("Garbage after directory header");

View File

@ -98,4 +98,77 @@ void ZipArchiveInfo::setZipComment(const std::string& comment)
} }
const char ZipArchiveInfo64::HEADER[ZipCommon::HEADER_SIZE] = {'\x50', '\x4b', '\x06', '\x06'};
const char ZipArchiveInfo64::LOCATOR_HEADER[ZipCommon::HEADER_SIZE] = {'\x50', '\x4b', '\x06', '\x07'};
ZipArchiveInfo64::ZipArchiveInfo64(std::istream& in, bool assumeHeaderRead):
_rawInfo(),
_startPos(in.tellg())
{
if (assumeHeaderRead)
_startPos -= ZipCommon::HEADER_SIZE;
parse(in, assumeHeaderRead);
}
ZipArchiveInfo64::ZipArchiveInfo64():
_rawInfo(),
_startPos(0)
{
std::memset(_rawInfo, 0, FULL_HEADER_SIZE);
std::memcpy(_rawInfo, HEADER, ZipCommon::HEADER_SIZE);
ZipUtil::set64BitValue(FULL_HEADER_SIZE - (RECORDSIZE_POS + RECORDSIZE_SIZE), _rawInfo, RECORDSIZE_POS);
std::memset(_locInfo, 0, FULL_LOCATOR_SIZE);
std::memcpy(_locInfo, LOCATOR_HEADER, ZipCommon::HEADER_SIZE);
setRequiredVersion(4, 5);
}
ZipArchiveInfo64::~ZipArchiveInfo64()
{
}
void ZipArchiveInfo64::parse(std::istream& inp, bool assumeHeaderRead)
{
if (!assumeHeaderRead)
{
inp.read(_rawInfo, ZipCommon::HEADER_SIZE);
}
else
{
std::memcpy(_rawInfo, HEADER, ZipCommon::HEADER_SIZE);
}
poco_assert (std::memcmp(_rawInfo, HEADER, ZipCommon::HEADER_SIZE) == 0);
std::memset(_rawInfo + ZipCommon::HEADER_SIZE, 0, FULL_HEADER_SIZE - ZipCommon::HEADER_SIZE);
// read the rest of the header
Poco::UInt64 offset = RECORDSIZE_POS;
inp.read(_rawInfo + ZipCommon::HEADER_SIZE, RECORDSIZE_SIZE);
offset += RECORDSIZE_SIZE;
Poco::UInt64 len = ZipUtil::get64BitValue(_rawInfo, RECORDSIZE_POS);
if(len <= FULL_HEADER_SIZE - offset) {
inp.read(_rawInfo + offset, len);
ZipUtil::set64BitValue(FULL_HEADER_SIZE - offset, _rawInfo, RECORDSIZE_POS);
} else {
inp.read(_rawInfo + offset, FULL_HEADER_SIZE - offset);
len -= (FULL_HEADER_SIZE - offset);
Poco::Buffer<char> xtra(len);
inp.read(xtra.begin(), len);
_extraField = std::string(xtra.begin(), len);
ZipUtil::set64BitValue(FULL_HEADER_SIZE + len - offset, _rawInfo, RECORDSIZE_POS);
}
inp.read(_locInfo, FULL_LOCATOR_SIZE);
poco_assert (std::memcmp(_locInfo, LOCATOR_HEADER, ZipCommon::HEADER_SIZE) == 0);
}
std::string ZipArchiveInfo64::createHeader() const
{
std::string result(_rawInfo, FULL_HEADER_SIZE);
result.append(_extraField);
result.append(_locInfo, FULL_LOCATOR_SIZE);
return result;
}
} } // namespace Poco::Zip } } // namespace Poco::Zip

View File

@ -56,4 +56,38 @@ ZipDataInfo::~ZipDataInfo()
} }
const char ZipDataInfo64::HEADER[ZipCommon::HEADER_SIZE] = {'\x50', '\x4b', '\x07', '\x08'};
ZipDataInfo64::ZipDataInfo64():
_rawInfo(),
_valid(true)
{
std::memcpy(_rawInfo, HEADER, ZipCommon::HEADER_SIZE);
std::memset(_rawInfo+ZipCommon::HEADER_SIZE, 0, FULLHEADER_SIZE - ZipCommon::HEADER_SIZE);
_valid = true;
}
ZipDataInfo64::ZipDataInfo64(std::istream& in, bool assumeHeaderRead):
_rawInfo(),
_valid(false)
{
if (assumeHeaderRead)
std::memcpy(_rawInfo, HEADER, ZipCommon::HEADER_SIZE);
else
in.read(_rawInfo, ZipCommon::HEADER_SIZE);
poco_assert (std::memcmp(_rawInfo, HEADER, ZipCommon::HEADER_SIZE) == 0);
// now copy the rest of the header
in.read(_rawInfo+ZipCommon::HEADER_SIZE, FULLHEADER_SIZE - ZipCommon::HEADER_SIZE);
_valid = (!in.eof() && in.good());
}
ZipDataInfo64::~ZipDataInfo64()
{
}
} } // namespace Poco::Zip } } // namespace Poco::Zip

View File

@ -10,7 +10,27 @@
// Copyright (c) 2007, Applied Informatics Software Engineering GmbH. // Copyright (c) 2007, Applied Informatics Software Engineering GmbH.
// and Contributors. // and Contributors.
// //
// SPDX-License-Identifier: BSL-1.0 // Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
// //
@ -33,6 +53,7 @@ ZipFileInfo::ZipFileInfo(const ZipLocalFileHeader& header):
_crc32(0), _crc32(0),
_compressedSize(0), _compressedSize(0),
_uncompressedSize(0), _uncompressedSize(0),
_localHeaderOffset(0),
_fileName(), _fileName(),
_lastModifiedAt(), _lastModifiedAt(),
_extraField() _extraField()
@ -51,7 +72,9 @@ ZipFileInfo::ZipFileInfo(const ZipLocalFileHeader& header):
setFileName(header.getFileName()); setFileName(header.getFileName());
if (getHostSystem() == ZipCommon::HS_UNIX) if (getHostSystem() == ZipCommon::HS_UNIX)
setUnixAttributes(); setUnixAttributes();
_rawInfo[GENERAL_PURPOSE_POS+1] |= 0x08; // Set "language encoding flag" to indicate that filenames and paths are in UTF-8. ZipFileInfo::ZipFileInfo(const ZipLocalFileHeader& header):
} }
@ -60,6 +83,7 @@ ZipFileInfo::ZipFileInfo(std::istream& in, bool assumeHeaderRead):
_crc32(0), _crc32(0),
_compressedSize(0), _compressedSize(0),
_uncompressedSize(0), _uncompressedSize(0),
_localHeaderOffset(0),
_fileName(), _fileName(),
_lastModifiedAt(), _lastModifiedAt(),
_extraField() _extraField()
@ -102,6 +126,25 @@ void ZipFileInfo::parse(std::istream& inp, bool assumeHeaderRead)
Poco::Buffer<char> xtra(len); Poco::Buffer<char> xtra(len);
inp.read(xtra.begin(), len); inp.read(xtra.begin(), len);
_extraField = std::string(xtra.begin(), len); _extraField = std::string(xtra.begin(), len);
char* ptr = xtra.begin();
while(ptr <= xtra.begin() + len - 4) {
Poco::UInt16 id = ZipUtil::get16BitValue(ptr, 0); ptr +=2;
Poco::UInt16 size = ZipUtil::get16BitValue(ptr, 0); ptr += 2;
if(id == ZipCommon::ZIP64_EXTRA_ID) {
poco_assert(size >= 8);
if(getUncompressedSizeFromHeader() == ZipCommon::ZIP64_MAGIC) {
setUncompressedSize(ZipUtil::get64BitValue(ptr, 0)); size -= 8; ptr += 8;
}
if(size >= 8 && getCompressedSizeFromHeader() == ZipCommon::ZIP64_MAGIC) {
setCompressedSize(ZipUtil::get64BitValue(ptr, 0)); size -= 8; ptr += 8;
}
if(size >= 8 && getOffsetFromHeader() == ZipCommon::ZIP64_MAGIC) {
setOffset(ZipUtil::get64BitValue(ptr, 0)); size -= 8; ptr += 8;
}
} else {
ptr += size;
}
}
} }
len = getFileCommentLength(); len = getFileCommentLength();
if (len > 0) if (len > 0)

View File

@ -10,7 +10,27 @@
// Copyright (c) 2007, Applied Informatics Software Engineering GmbH. // Copyright (c) 2007, Applied Informatics Software Engineering GmbH.
// and Contributors. // and Contributors.
// //
// SPDX-License-Identifier: BSL-1.0 // Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
// //
@ -33,8 +53,10 @@ const char ZipLocalFileHeader::HEADER[ZipCommon::HEADER_SIZE] = {'\x50', '\x4b',
ZipLocalFileHeader::ZipLocalFileHeader(const Poco::Path& fileName, ZipLocalFileHeader::ZipLocalFileHeader(const Poco::Path& fileName,
const Poco::DateTime& lastModifiedAt, const Poco::DateTime& lastModifiedAt,
ZipCommon::CompressionMethod cm, ZipCommon::CompressionMethod cm,
ZipCommon::CompressionLevel cl): ZipCommon::CompressionLevel cl,
_rawHeader(), bool forceZip64):
_forceZip64(forceZip64),
_rawHeader(),
_startPos(-1), _startPos(-1),
_endPos(-1), _endPos(-1),
_fileName(), _fileName(),
@ -66,6 +88,7 @@ ZipLocalFileHeader::ZipLocalFileHeader(const Poco::Path& fileName,
ZipLocalFileHeader::ZipLocalFileHeader(std::istream& inp, bool assumeHeaderRead, ParseCallback& callback): ZipLocalFileHeader::ZipLocalFileHeader(std::istream& inp, bool assumeHeaderRead, ParseCallback& callback):
_forceZip64(false),
_rawHeader(), _rawHeader(),
_startPos(inp.tellg()), _startPos(inp.tellg()),
_endPos(-1), _endPos(-1),
@ -76,7 +99,7 @@ ZipLocalFileHeader::ZipLocalFileHeader(std::istream& inp, bool assumeHeaderRead,
_compressedSize(0), _compressedSize(0),
_uncompressedSize(0) _uncompressedSize(0)
{ {
poco_assert_dbg( (EXTRAFIELD_POS+EXTRAFIELD_LENGTH) == FULLHEADER_SIZE); poco_assert_dbg( (EXTRA_FIELD_POS+EXTRA_FIELD_LENGTH) == FULLHEADER_SIZE);
if (assumeHeaderRead) if (assumeHeaderRead)
_startPos -= ZipCommon::HEADER_SIZE; _startPos -= ZipCommon::HEADER_SIZE;
@ -89,10 +112,19 @@ ZipLocalFileHeader::ZipLocalFileHeader(std::istream& inp, bool assumeHeaderRead,
{ {
if (searchCRCAndSizesAfterData()) if (searchCRCAndSizesAfterData())
{ {
ZipDataInfo nfo(inp, false); char header[ZipCommon::HEADER_SIZE]={'\x00', '\x00', '\x00', '\x00'};
setCRC(nfo.getCRC32()); inp.read(header, ZipCommon::HEADER_SIZE);
setCompressedSize(nfo.getCompressedSize()); if(::memcmp(header, ZipDataInfo64::HEADER, sizeof(header)) == 0) {
setUncompressedSize(nfo.getUncompressedSize()); ZipDataInfo64 nfo(inp, true);
setCRC(nfo.getCRC32());
setCompressedSize(nfo.getCompressedSize());
setUncompressedSize(nfo.getUncompressedSize());
} else {
ZipDataInfo nfo(inp, true);
setCRC(nfo.getCRC32());
setCompressedSize(nfo.getCompressedSize());
setUncompressedSize(nfo.getUncompressedSize());
}
} }
} }
else else
@ -122,28 +154,46 @@ void ZipLocalFileHeader::parse(std::istream& inp, bool assumeHeaderRead)
poco_assert (std::memcmp(_rawHeader, HEADER, ZipCommon::HEADER_SIZE) == 0); poco_assert (std::memcmp(_rawHeader, HEADER, ZipCommon::HEADER_SIZE) == 0);
// read the rest of the header // read the rest of the header
inp.read(_rawHeader + ZipCommon::HEADER_SIZE, FULLHEADER_SIZE - ZipCommon::HEADER_SIZE); inp.read(_rawHeader + ZipCommon::HEADER_SIZE, FULLHEADER_SIZE - ZipCommon::HEADER_SIZE);
if (!(_rawHeader[VERSION_POS + 1]>= ZipCommon::HS_FAT && _rawHeader[VERSION_POS + 1] < ZipCommon::HS_UNUSED)) poco_assert (_rawHeader[VERSION_POS + 1]>= ZipCommon::HS_FAT && _rawHeader[VERSION_POS + 1] < ZipCommon::HS_UNUSED);
throw Poco::DataFormatException("bad ZIP file header", "invalid version"); poco_assert (getMajorVersionNumber() <= 4); // Allow for Zip64 version 4.5
if (ZipUtil::get16BitValue(_rawHeader, COMPR_METHOD_POS) >= ZipCommon::CM_UNUSED) poco_assert (ZipUtil::get16BitValue(_rawHeader, COMPR_METHOD_POS) < ZipCommon::CM_UNUSED);
throw Poco::DataFormatException("bad ZIP file header", "invalid compression method");
parseDateTime(); parseDateTime();
Poco::UInt16 len = getFileNameLength(); Poco::UInt16 len = getFileNameLength();
Poco::Buffer<char> buf(len); Poco::Buffer<char> buf(len);
inp.read(buf.begin(), len); inp.read(buf.begin(), len);
_fileName = std::string(buf.begin(), len); _fileName = std::string(buf.begin(), len);
if (hasExtraField())
{
len = getExtraFieldLength();
Poco::Buffer<char> xtra(len);
inp.read(xtra.begin(), len);
_extraField = std::string(xtra.begin(), len);
}
if (!searchCRCAndSizesAfterData()) if (!searchCRCAndSizesAfterData())
{ {
_crc32 = getCRCFromHeader(); _crc32 = getCRCFromHeader();
_compressedSize = getCompressedSizeFromHeader(); _compressedSize = getCompressedSizeFromHeader();
_uncompressedSize = getUncompressedSizeFromHeader(); _uncompressedSize = getUncompressedSizeFromHeader();
} }
if (hasExtraField())
{
len = getExtraFieldLength();
Poco::Buffer<char> xtra(len);
inp.read(xtra.begin(), len);
_extraField = std::string(xtra.begin(), len);
char* ptr = xtra.begin();
while(ptr <= xtra.begin() + len - 4) {
Poco::UInt16 id = ZipUtil::get16BitValue(ptr, 0); ptr +=2;
Poco::UInt16 size = ZipUtil::get16BitValue(ptr, 0); ptr += 2;
if(id == ZipCommon::ZIP64_EXTRA_ID) {
poco_assert(size >= 8);
if(getUncompressedSizeFromHeader() == ZipCommon::ZIP64_MAGIC) {
setUncompressedSize(ZipUtil::get64BitValue(ptr, 0)); size -= 8; ptr += 8;
}
if(size >= 8 && getCompressedSizeFromHeader() == ZipCommon::ZIP64_MAGIC) {
setCompressedSize(ZipUtil::get64BitValue(ptr, 0)); size -= 8; ptr += 8;
}
} else {
ptr += size;
}
}
}
} }
@ -204,6 +254,9 @@ void ZipLocalFileHeader::init( const Poco::Path& fName,
} }
else else
setCompressionMethod(ZipCommon::CM_STORE); setCompressionMethod(ZipCommon::CM_STORE);
if(_forceZip64)
setZip64Data();
_rawHeader[GENERAL_PURPOSE_POS+1] |= 0x08; // Set "language encoding flag" to indicate that filenames and paths are in UTF-8.
} }

View File

@ -135,6 +135,8 @@ ZipStreamBuf::ZipStreamBuf(std::ostream& ostr, ZipLocalFileHeader& fileEntry, bo
else throw Poco::NotImplementedException("Unsupported compression method"); else throw Poco::NotImplementedException("Unsupported compression method");
// now write the header to the ostr! // now write the header to the ostr!
if(fileEntry.needsZip64())
fileEntry.setZip64Data();
std::string header = fileEntry.createHeader(); std::string header = fileEntry.createHeader();
ostr.write(header.c_str(), static_cast<std::streamsize>(header.size())); ostr.write(header.c_str(), static_cast<std::streamsize>(header.size()));
} }
@ -198,8 +200,9 @@ int ZipStreamBuf::writeToDevice(const char* buffer, std::streamsize length)
} }
void ZipStreamBuf::close() void ZipStreamBuf::close(Poco::UInt64& extraDataSize)
{ {
extraDataSize = 0;
if (_ptrOBuf && _pHeader) if (_ptrOBuf && _pHeader)
{ {
_ptrOBuf->flush(); _ptrOBuf->flush();
@ -217,20 +220,33 @@ void ZipStreamBuf::close()
// or fix the crc entries // or fix the crc entries
if (_pHeader->searchCRCAndSizesAfterData()) if (_pHeader->searchCRCAndSizesAfterData())
{ {
ZipDataInfo info; if(_pHeader->needsZip64()) {
info.setCRC32(_crc32.checksum()); ZipDataInfo64 info;
info.setUncompressedSize(_bytesWritten); info.setCRC32(_crc32.checksum());
info.setCompressedSize(static_cast<Poco::UInt32>(_ptrOHelper->bytesWritten())); info.setUncompressedSize(_bytesWritten);
_pOstr->write(info.getRawHeader(), static_cast<std::streamsize>(info.getFullHeaderSize())); info.setCompressedSize(_ptrOHelper->bytesWritten());
extraDataSize = info.getFullHeaderSize();
_pOstr->write(info.getRawHeader(), static_cast<std::streamsize>(extraDataSize));
} else {
ZipDataInfo info;
info.setCRC32(_crc32.checksum());
info.setUncompressedSize(static_cast<Poco::UInt32>(_bytesWritten));
info.setCompressedSize(static_cast<Poco::UInt32>(_ptrOHelper->bytesWritten()));
extraDataSize = info.getFullHeaderSize();
_pOstr->write(info.getRawHeader(), static_cast<std::streamsize>(extraDataSize));
}
} }
else else
{ {
poco_check_ptr (_pHeader); poco_check_ptr (_pHeader);
_pHeader->setCRC(_crc32.checksum()); _pHeader->setCRC(_crc32.checksum());
_pHeader->setUncompressedSize(_bytesWritten); _pHeader->setUncompressedSize(_bytesWritten);
_pHeader->setCompressedSize(static_cast<Poco::UInt32>(_ptrOHelper->bytesWritten())); _pHeader->setCompressedSize(_ptrOHelper->bytesWritten());
_pOstr->seekp(_pHeader->getStartPos(), std::ios_base::beg); _pOstr->seekp(_pHeader->getStartPos(), std::ios_base::beg);
poco_assert (*_pOstr); poco_assert (*_pOstr);
_pHeader->setStartPos(_pHeader->getStartPos()); // This resets EndPos now that compressed Size is known
if(_pHeader->hasExtraField()) // Update sizes in header extension.
_pHeader->setZip64Data();
std::string header = _pHeader->createHeader(); std::string header = _pHeader->createHeader();
_pOstr->write(header.c_str(), static_cast<std::streamsize>(header.size())); _pOstr->write(header.c_str(), static_cast<std::streamsize>(header.size()));
_pOstr->seekp(0, std::ios_base::end); _pOstr->seekp(0, std::ios_base::end);
@ -297,10 +313,10 @@ ZipOutputStream::~ZipOutputStream()
} }
void ZipOutputStream::close() void ZipOutputStream::close(Poco::UInt64& extraDataSize)
{ {
flush(); flush();
_buf.close(); _buf.close(extraDataSize);
} }

View File

@ -12,14 +12,19 @@
#include "CompressTest.h" #include "CompressTest.h"
#include "ZipTest.h" #include "ZipTest.h"
#include "Poco/Buffer.h"
#include "Poco/Zip/Compress.h" #include "Poco/Zip/Compress.h"
#include "Poco/Zip/ZipManipulator.h" #include "Poco/Zip/ZipManipulator.h"
#include "Poco/File.h" #include "Poco/File.h"
#include "Poco/FileStream.h" #include "Poco/FileStream.h"
#include "CppUnit/TestCaller.h" #include "CppUnit/TestCaller.h"
#include "CppUnit/TestSuite.h" #include "CppUnit/TestSuite.h"
#include <fstream> #include <fstream>
#undef min
#include <algorithm>
using namespace Poco::Zip; using namespace Poco::Zip;
@ -133,6 +138,50 @@ void CompressTest::testSetZipComment()
assert(a.getZipComment() == comment); assert(a.getZipComment() == comment);
} }
void CompressTest::createDataFile(const std::string& path, Poco::UInt64 size) {
std::ofstream out(path, std::ios::binary | std::ios::trunc);
assert( ! out.fail() );
Poco::Buffer<char> buffer(MB);
for(int i = 0; size != 0; i++) {
std::memset(buffer.begin(), i, buffer.size());
Poco::UInt64 bytesToWrite = std::min(size, buffer.size());
out.write(buffer.begin(), bytesToWrite);
assert( ! out.fail() );
size -= bytesToWrite;
}
out.flush();
assert( ! out.fail() );
out.close();
assert( ! out.fail() );
}
void CompressTest::testZip64()
{
std::map<std::string, Poco::UInt64> files;
files["data1.bin"] = static_cast<Poco::UInt64>(MB)*4096+1;
files["data2.bin"] = static_cast<Poco::UInt64>(MB)*16;
files["data3.bin"] = static_cast<Poco::UInt64>(MB)*4096-1;
for(std::map<std::string, Poco::UInt64>::const_iterator it = files.begin(); it != files.end(); it++) {
createDataFile(it->first, it->second);
}
std::ofstream out("zip64.zip", std::ios::binary | std::ios::trunc);
Compress c(out, true, true);
for(std::map<std::string, Poco::UInt64>::const_iterator it = files.begin(); it != files.end(); it++) {
const std::string& path = it->first;
c.addFile(path, path, ZipCommon::CM_STORE);
}
ZipArchive a(c.close());
for(std::map<std::string, Poco::UInt64>::const_iterator it = files.begin(); it != files.end(); it++) {
const std::string& path = it->first;
Poco::UInt64 size = it->second;
ZipArchive::FileHeaders::const_iterator it2 = a.findHeader(path);
assert (it2 != a.headerEnd());
const Poco::Zip::ZipLocalFileHeader& file = it2->second;
assert(file.getUncompressedSize() == size);
assert(file.getCompressedSize() == size);
}
}
void CompressTest::setUp() void CompressTest::setUp()
{ {
@ -154,6 +203,7 @@ CppUnit::Test* CompressTest::suite()
CppUnit_addTest(pSuite, CompressTest, testManipulatorDel); CppUnit_addTest(pSuite, CompressTest, testManipulatorDel);
CppUnit_addTest(pSuite, CompressTest, testManipulatorReplace); CppUnit_addTest(pSuite, CompressTest, testManipulatorReplace);
CppUnit_addTest(pSuite, CompressTest, testSetZipComment); CppUnit_addTest(pSuite, CompressTest, testSetZipComment);
CppUnit_addTest(pSuite, CompressTest, testZip64);
return pSuite; return pSuite;
} }

View File

@ -19,7 +19,6 @@
#include "Poco/Zip/Zip.h" #include "Poco/Zip/Zip.h"
#include "CppUnit/TestCase.h" #include "CppUnit/TestCase.h"
class CompressTest: public CppUnit::TestCase class CompressTest: public CppUnit::TestCase
{ {
public: public:
@ -33,11 +32,16 @@ public:
void testManipulatorReplace(); void testManipulatorReplace();
void testSetZipComment(); void testSetZipComment();
static const Poco::UInt64 MB = (1024*1024);
void createDataFile(const std::string& path, Poco::UInt64 size);
void testZip64();
void setUp(); void setUp();
void tearDown(); void tearDown();
static CppUnit::Test* suite(); static CppUnit::Test* suite();
private: private:
}; };

View File

@ -25,10 +25,12 @@
#include "Poco/StreamCopier.h" #include "Poco/StreamCopier.h"
#include "CppUnit/TestCaller.h" #include "CppUnit/TestCaller.h"
#include "CppUnit/TestSuite.h" #include "CppUnit/TestSuite.h"
#undef min
#include <algorithm>
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
using namespace Poco::Zip; using namespace Poco::Zip;
@ -58,8 +60,8 @@ void ZipTest::testSkipSingleFile()
ZipCommon::CompressionMethod cm = hdr.getCompressionMethod(); ZipCommon::CompressionMethod cm = hdr.getCompressionMethod();
assert (!hdr.isEncrypted()); assert (!hdr.isEncrypted());
Poco::DateTime aDate = hdr.lastModifiedAt(); Poco::DateTime aDate = hdr.lastModifiedAt();
Poco::UInt32 cS = hdr.getCompressedSize(); Poco::UInt64 cS = hdr.getCompressedSize();
Poco::UInt32 uS = hdr.getUncompressedSize(); Poco::UInt64 uS = hdr.getUncompressedSize();
const std::string& fileName = hdr.getFileName(); const std::string& fileName = hdr.getFileName();
} }
@ -163,6 +165,44 @@ void ZipTest::testDecompressFlat()
assert (!dec.mapping().empty()); assert (!dec.mapping().empty());
} }
void ZipTest::verifyDataFile(const std::string& path, Poco::UInt64 size) {
std::ifstream in(path, std::ios::binary);
assert( ! in.fail() );
Poco::Buffer<char> buffer1(MB);
Poco::Buffer<char> buffer2(MB);
for(int i = 0; size != 0; i++) {
std::memset(buffer1.begin(), i, buffer1.size());
std::memset(buffer2.begin(), 0, buffer2.size());
Poco::UInt64 bytesToRead = std::min(size, buffer2.size());
in.read(buffer2.begin(), bytesToRead);
assert( ! in.fail() );
assert( std::memcmp(buffer1.begin(), buffer2.begin(), bytesToRead) == 0);
size -= bytesToRead;
}
char c;
in.read(&c, 1);
assert ( in.eof() );
}
void ZipTest::testDecompressZip64()
{
std::map<std::string, Poco::UInt64> files;
files["data1.bin"] = static_cast<Poco::UInt64>(MB)*4096+1;
files["data2.bin"] = static_cast<Poco::UInt64>(MB)*16;
files["data3.bin"] = static_cast<Poco::UInt64>(MB)*4096-1;
for(std::map<std::string, Poco::UInt64>::const_iterator it = files.begin(); it != files.end(); it++) {
Poco::File file(it->first);
if(file.exists())
file.remove();
}
std::ifstream in("zip64.zip", std::ios::binary);
Decompress c(in, ".");
c.decompressAllFiles();
for(std::map<std::string, Poco::UInt64>::const_iterator it = files.begin(); it != files.end(); it++) {
verifyDataFile(it->first, it->second);
}
}
void ZipTest::onDecompressError(const void* pSender, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string>& info) void ZipTest::onDecompressError(const void* pSender, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string>& info)
{ {
@ -191,5 +231,7 @@ CppUnit::Test* ZipTest::suite()
CppUnit_addTest(pSuite, ZipTest, testDecompressFlat); CppUnit_addTest(pSuite, ZipTest, testDecompressFlat);
CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterData); CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterData);
CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterDataWithArchive); CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterDataWithArchive);
CppUnit_addTest(pSuite, ZipTest, testDecompressZip64);
return pSuite; return pSuite;
} }

View File

@ -35,6 +35,10 @@ public:
void testDecompressFlat(); void testDecompressFlat();
static const Poco::UInt64 MB = (1024*1024);
void verifyDataFile(const std::string& path, Poco::UInt64 size);
void testDecompressZip64();
void setUp(); void setUp();
void tearDown(); void tearDown();